diff --git a/.agents/skills/github-pr-mirror/SKILL.md b/.agents/skills/github-pr-mirror/SKILL.md new file mode 100644 index 0000000000..e04c567727 --- /dev/null +++ b/.agents/skills/github-pr-mirror/SKILL.md @@ -0,0 +1,153 @@ +--- +name: github-pr-mirror +description: Mirror an external GitHub pull request into the internal GraalPython Bitbucket review flow, including OCA label checks, Jira creation or reuse, preserving PR commits, pre-commit cleanup, and handoff to the shared GraalPython Bitbucket PR flow for PR creation, Graal Bot tasks, gates, and fixes. +--- + +# GitHub PR Mirror + +Use this skill when asked to mirror, import, port, or copy a GitHub pull request into Bitbucket for GraalPython review. + +This workflow intentionally preserves the original GitHub PR commits. Do not rebase, squash, amend, or otherwise rewrite the contributor's commits unless the user explicitly tells you to abandon GitHub auto-closing behavior. + +## Companion Skills + +- Use the `jira` skill when creating or transitioning Jira issues. +- Use the `graalpython-bitbucket-pr` skill after the mirror branch is ready, to create the Bitbucket PR, handle Graal Bot tasks, start/watch gates, and fix or report gate failures. + +## Inputs + +Required: +- GitHub PR number or URL. + +Optional: +- Existing Jira issue key, such as `GR-12345`. +- Bitbucket target branch. Default to the GitHub PR base branch, normally `master`. +- Bitbucket remote. Default to `origin`. +- GitHub remote. Default to `github`; if absent, use the remote matching `github.com/graalvm/graalpython`. +- Bitbucket project/repository. Default to `G` / `graalpython`. + +## Workflow + +### 1. Inspect the GitHub PR and verify OCA + +Fetch GitHub PR metadata without modifying the repository: + +```bash +gh pr view --repo graalvm/graalpython --json number,title,url,labels,baseRefName,headRefOid +``` + +Abort if the labels do not include exactly `OCA Verified`. Tell the user the PR is not mirrorable yet because OCA is missing. + +Record: +- `PR_NUMBER` +- `PR_TITLE` +- `PR_URL` +- `BASE_BRANCH` +- `HEAD_SHA` + +### 2. Create or reuse the Jira issue + +If the user supplied a Jira issue, use it. Otherwise create one: + +- Project: `GR` +- Component: `Python` +- Summary: `GitHub PR#: ` +- Description: the GitHub PR URL only +- Assignee: current user. If not already known, derive from `git config user.email` + +Use a temporary JSON template for `gdev-cli jira create`, then remove the template after creation. Adapt the assignee field to the accepted Jira schema if the CLI rejects the first attempt. + +Example template shape: + +```json +{ + "fields": { + "project": { "key": "GR" }, + "summary": "GitHub PR#: ", + "description": "", + "issuetype": { "name": "Task" }, + "components": [{ "name": "Python" }], + "assignee": { "emailAddress": "" } + } +} +``` + +After creation or reuse, transition the issue to In Progress: + +```bash +gdev-cli jira transition --issues -t "Start Progress" --force +``` + +If Jira reports a different valid transition name for starting work, use that name. + +### 3. Fetch the PR and create the mirror branch + +Use a branch named exactly: + +```text +github-pr/ +``` + +Do not reuse a dirty or unrelated branch. If the branch already exists, inspect it; only continue if it already points at the same GitHub PR history or the user confirms replacement. + +Fetch and branch from the GitHub PR head: + +```bash +git fetch pull//head +git switch -c github-pr/ FETCH_HEAD +``` + +Verify `HEAD` is the GitHub PR head SHA from step 1: + +```bash +test "$(git rev-parse HEAD)" = "" +``` + +Do not rebase. Do not squash. Do not amend original commits. + +Check that there are no merge conflicts using `git merge-tree`. If there are, fetch and merge the target branch without fast-forwarding and resolve the conflicts. + +### 4. Run pre-commit on the PR range + +Fetch the base branch and compute the original PR commit range: + +```bash +git fetch +BASE_REF="$(git merge-base HEAD /)" +pre-commit run --from-ref "$BASE_REF" --to-ref HEAD +``` + +If pre-commit modified files, review the diff and create a new follow-up commit on top of the GitHub PR commits: + +```bash +git status --short +git diff +git add +git commit -m "[GR-] Apply pre-commit fixes for GitHub PR#" +``` + +Only commit mechanical pre-commit output here. If pre-commit reveals non-mechanical problems, fix them in a separate follow-up commit when trivial; otherwise stop and ask the user. + +### 5. Create the Bitbucket PR and follow gates + +Use the `graalpython-bitbucket-pr` skill with these inputs: +- Source branch: `github-pr/` +- Bitbucket remote: `` +- Target branch: `` +- Title: `[] GitHub PR#: ` +- Description: `Mirrors GitHub for internal review.` +- Project/repository: `G` / `graalpython` +- Reviewers: repo-level default reviewers + +Let that skill create the PR, handle Graal Bot comments/tasks, start or verify gates, watch gates, and fix or report failures. + +## Final Report + +Always report: +- Bitbucket PR URL +- Jira issue key or URL +- Gate status and any unresolved failures or bot tasks + +## Guardrails +- Don't comment on the github PR unless asked. Never mention the internal bitbucket/buildbot/etc URLs in comments on github. +- After handing off to `graalpython-bitbucket-pr`, do not stop monitoring the gates until they finish, fail, or become blocked by tooling. diff --git a/.agents/skills/graalpython-bitbucket-pr/SKILL.md b/.agents/skills/graalpython-bitbucket-pr/SKILL.md new file mode 100644 index 0000000000..af57a9e896 --- /dev/null +++ b/.agents/skills/graalpython-bitbucket-pr/SKILL.md @@ -0,0 +1,124 @@ +--- +name: graalpython-bitbucket-pr +description: Create or continue a GraalPython Bitbucket pull request and drive it through Graal Bot tasks, gate start, gate monitoring, failure investigation, fixes, pushes, and gate reruns. Use after a branch is ready for internal GraalPython review, or when an automation command has already created the PR and the remaining work is task cleanup and gate follow-up. +--- + +# GraalPython Bitbucket PR + +## Overview +Create or continue a GraalPython Bitbucket PR, resolve administrative Graal Bot tasks, start or verify gates, watch gates, and fix or report failures. + +## Companion Skills +- Use the `bitbucket` skill for PR creation, comments, and tasks. +- Use the `buildbot` skill for starting, watching, rerunning, and investigating gates. + +## Inputs +Required for new PRs: +- Source branch already pushed or ready to push. +- Target branch. +- PR title. +- PR description. +- Jira key in the PR title. + +Required for existing PRs: +- Bitbucket PR ID or URL. + +Defaults: +- Project/repository: `G` / `graalpython`. +- Reviewers: use the repo-level default reviewers unless the caller supplies a different list. +- Target branch: master + +## Workflow + +### 1. Push and create the PR, if needed + +If the source branch is not pushed yet: + +```bash +git push +``` + +Create the Bitbucket PR: + +```bash +gdev-cli bitbucket create-pr \ + -p G \ + -r graalpython \ + -fb \ + -tb \ + -t "" \ + -rv "" \ + -d "" \ + --gate +``` + +Capture the Bitbucket PR URL and ID. If `--gate` is not accepted or fails transiently, create the PR first, then start gates with: + +```bash +gdev-cli buildbot start-gate -p G -r graalpython -pr +``` + +If the PR already exists, capture the PR URL and ID, then continue with task handling and gate checks. + +### 2. Handle Graal Bot comments and tasks + +Wait about 15 seconds after PR creation, then list comments and tasks: + +```bash +gdev-cli bitbucket comment list -p G -r graalpython -pr --all --json +gdev-cli bitbucket task list -p G -r graalpython -pr --json +``` + +For tasks or comments authored by Graal Bot: +- Resolve tasks that are clearly administrative and already satisfied by the PR workflow. +- There should be a task regarding changelog. Changelog items are usually not needed for bugfixes and compatibility improvements. + They are needed for added context options, changed public API's in `polyglot` module or large features. If a changelog item is needed + but not present, suggest one and notify the user. + +Resolve a task only when justified: + +```bash +gdev-cli bitbucket task resolve -p G -r graalpython -pr -cm +``` + +### 3. Watch gates + +Use sparse polling because full gates can take about an hour. Prefer structured output for status decisions: + +```bash +gdev-cli buildbot gate-overview -p G -r graalpython -pr --json +gdev-cli buildbot gate-builds -p G -r graalpython -pr --json +``` + +Human-readable output is fine for reporting, but avoid parsing it when JSON is available. + +Suggested cadence: +- First check after PR creation and bot handling. +- Then every 10 minutes while many jobs are running. +- When fewer than about 5 jobs remain, poll every 5 minutes. +- Poll silently while gates are still running. +- If there are failures, try to diagnose them as soon as they appear. + +Treat the gate as successful only when there are no running and no failed gate builds. Other PR vetoes, such as reviewer approval, merge queue state, or GitHub mirroring consideration, are not gate failures. + +### 4. Fix or report gate failures + +If gates fail: +- Inspect failing build logs with the `buildbot` skill. +- Fix trivial issues yourself, such as style output, generated pre-commit fallout, missing `@TruffleBoundary`, obvious test-selector mistakes, or small import/order problems. +- Commit fixes on top of the PR branch, push again, and restart or rerun gates as appropriate. +- Report non-trivial semantic failures, broad compatibility failures, or failures that require product judgment to the user with the Bitbucket PR URL, Jira key, and concise failure summary. +- Many gate failures are transient. Use your judgement to determine if a failure might be transient and if it's the case, restart the gate and go back to polling. +- Typical signs of a transient failure: + - Infrastructure issues in the worker setup, git checkout or cleanup + - Single failed test on a single platform that succeeded on other platforms + - Test failures in tests involving weakrefs, subprocesses or multi-threading in PRs that didn't touch those subsystems. + +Do not leave a long sleep process running after an interruption or handoff. If monitoring is interrupted, clean up any background sleep/polling process you started, then resume with a fresh status check. + +## Final Report +Always report: +- Bitbucket PR URL +- Gate status +- Any unresolved failures or Graal Bot tasks +- Any transient issues encountered, with links to failed builds diff --git a/.agents/skills/jira/SKILL.md b/.agents/skills/jira/SKILL.md new file mode 100644 index 0000000000..9d170658ff --- /dev/null +++ b/.agents/skills/jira/SKILL.md @@ -0,0 +1,171 @@ +--- +name: jira +description: Deal with Jira tickets autonomously +--- + +# Jira + +## Overview + +Deal with Jira tickets, pull their description and any linked context, search, +update, reproduce, potentially fix and/or close them. Go on with this workflow +to the end unless you are actually blocked or get to one of the points where +the workflow tells you to wait for confirmation or ask something. + +### General Notes + +Typical fields you need to know: +* "components" is typically one of "Python", "Mx", "Infra", "Compiler", "Truffle" +* "issuetype" is typically "Task", "Bug (non BugDB)", "Testing", "Build Failure" +* "project" is typically "GR" +* "labels" is typically left empty when creating new issues + +### 1. Getting context + +To get the issue data, start with `gdev-cli`, for example: + + gdev-cli jira get-issue --json -id GR-72840 + +Read the description and follow any links that seem relevant. + +Run this in a subagent if possible and let it give you a summary. + +### 2. Check if there is work to do + +Issues may be stale, already solved, or no longer apply. Search the context and +logs for other potentially relevant keywords, use `gdev-cli jira search` to find +out if there are potentially other related issues, query the codebase and git +history and look for reproducers. + +Run this in a subagent if possible and let it give you a summary. + +### 3. Reproduce the issue + +It is PARAMOUNT to reproduce an issue first before changing code. You should +not give up trying to reproduce an issue until you are certain that it *cannot* +be reproduced or you spent at least a few hours and a million tokens on it. If +after that amount of work you have not reproduced the issue, STOP AND ASK for +guidance. + +Derive how to reproduce the issue from the logs and issues you find. This may +mean running existing tests in a loop if the issue appears transient, or +writing a new test in `graalpython/com.oracle.graal.python.tests/src/tests` or +just a script to reproduce it. Do NOT write new tests in +`graalpython/lib-python/3/tests`, if you want to add new tests, put them under +`graalpython/com.oracle.graal.python.tests/src/tests`. Tests should be run +using `mx graalpytest`. + +Reproducing something that fails rarely or only on another architecture or in +the CI may be tough, but can be achieved using `gdev-cli bitbucket` and running +in the gate, stress testing there. + + 1. Create a temporary branch. + 2. Potentially add a new CI job to run just the reproducer. + 3. Push to Bitbucket. + 4. Open a PR using gdev-cli bitbucket. + 5. Use the bitbucket buildbot REST API to request a merge commit `/rest/ci/1.0/base/projects/G/repos//pullRequest//mergeCommit`. You need a `Authorizatin: Bearer ` header. + Make sure to disable any proxy environment variables for the API call. You should be able to find the token by decoding the base64 token from the gdev-cli config. + 6. Wait a bit, the CI will create a new commit on a branch. The name of that branch is `_gate`. Use `git fetch` to see the branch update come in. + 7. Use the bitbucket buildbot REST API to request CI job enumeration for the HEAD commit of that `_gate` branch: `/rest/ci/1.0/base/projects/G/repos//enumerate/?branch=&force=false&toBranch=master` + 8. Wait a bit, the CI will enumerate the available CI jobs on that commit. + 9. Use gdev-cli bitbucket to start your reproducer job on that commit. + 10. Repeat steps 3 through 9 until you are satisfied with the reproducer. + +DO NOT STOP POLLING AND RETRYING UNTIL EITHER YOU REPRODUCE THE ISSUE, MORE +THAN 8 HOURS HAVE ELAPSED WHILE YOU TRIED, OR YOU HAVE USED AT LEAST AROUND 2 +MILLION TOKENS (you may estimate from the conversation history) WHILE TRYING! + +Make sure to decline the temporary reproducer PR once you are done with it +using `gdev-cli bitbucket`. + +### 4a. Fixing a reproducible issue. + +Once you have a reproducer (even if it may mean running something in a loop for +a while), you can try to make a plan to fix it. Use the sources and tools at +your disposal to investigate the issue. Present the plan and ask for additional +guidance before committing to a code change. + +When you implement a change, make sure to verify using the reproducer. + +### 4b. Fixing a non-reproducible issue. + +If you failed to reproduce an issue, spent time reasoning about it and form one +or more hypotheses. Present your hypotheses clearly, point to source code, +documentation, or web sources that lead you to believe something may fix an +issue. Then STOP AND ASK for guidance. + +### 5. Preparing a code change for merge + +Once a code change has been implemented and verified (either with reproducer or +by approval of the human user), it needs to be prepared for inclusion. + +Transition the Jira issue to be "In Progress" using `gdev-cli jira transition`. + +Make sure your changes are committed in reviewable, focused, incremental +commits. + +Run a subagent to REVIEW the code changes. Give it enough context to understand +why specific implementation decisions were made. Consider the subagent's +comments carefully, change the code where the subagent's comments make sense. + +Create a bitbucket PR + + 1. Push your branch. + 2. Open a PR using gdev-cli bitbucket with a title including the Jira issue ID, like "[GR-XXXXX] Short description of overall fix." + 3. Use the bitbucket buildbot REST API to request a merge commit `/rest/ci/1.0/base/projects/G/repos//pullRequest//mergeCommit`. You need a `Authorizatin: Bearer ` header. + Make sure to disable any proxy environment variables for the API call. You should be able to find the token by decoding the base64 token from the gdev-cli config. + 4. Wait a bit, the CI will create a new commit on a branch. The name of that branch is `_gate`. Use `git fetch` to see the branch update come in. + 5. Use the bitbucket buildbot REST API to request CI job enumeration for the HEAD commit of that `_gate` branch: `/rest/ci/1.0/base/projects/G/repos//enumerate/?branch=&force=false&toBranch=master` + 6. Wait a bit, the CI will enumerate the available CI jobs on that commit. + 7. Use gdev-cli bitbucket to start and watch the gate jobs on the HEAD commit of that `_gate` branch. They may take a few hours to finish, so poll sparingly. + 8. If there are failures, investigate them and try to fix them yourself on top of the PR. + 9. Repeat steps 1 through 8 until the gates pass or you need help from the human. + +### 6. Updating the Jira issue contents. + +Once you have determined that no code change is necessary, or a code change has +been implemented and the PR created, the Jira issue needs to be updated. + +You can do this in parallel while watching the Bitbucket PR from step 5. + +Add a comment using `gdev-cli jira comment` to the Jira issue, summarizing your +findings and any work you may have done. Do NOT use Attlassian markup, the +comment just ONLY be PLAIN TEXT. For paragraphs, just use double '\n'. You can +make plaintext lists by making lines begin with '* '. Do NOT use ADF, use raw +text, regardless of what the tool's help message says. + +Also decide yourself or confer with the human about whether this change needs +to be backported, and what the "fix version" assignment for the Jira label +should be. + +A typical template for a Jira issue update looks like this: + + { + "fields": { + "fixVersions": [ + { + "name": "graalvm-25.1.0" + } + ], + "labels": [ + "no-backport" + ] + } + } + +`fixVersions` could also be `n/a` when it's not really version specific like a +CI failure. The version name can be derived from the mx.graalpython/suite.py +version string, with `graalvm-` prepended. + +`labels` could also be `to-backport`, confer with the user which one to choose +unless it is obvious. + +Make sure not to delete an pre-existing fixVersions or labels. + +If the issue is already fixed, close it. + +### 7. Final report + +Finalize your report to the user: post the complete URL to the PR and the Jira, +summarize a last time what you found and did. + diff --git a/.agents/skills/pr-gate-check/SKILL.md b/.agents/skills/pr-gate-check/SKILL.md new file mode 100644 index 0000000000..e5b99c0399 --- /dev/null +++ b/.agents/skills/pr-gate-check/SKILL.md @@ -0,0 +1,61 @@ +--- +name: pr-gate-check +description: Check gate status for a Bitbucket PR by resolving the PR head commit, finding the gate merge commit, inspecting builds on that merge commit, and summarizing root-cause failures with actionable next steps. +--- + +# PR Gate Check + +## Overview +Use this workflow when asked for gate status of a PR. Usually the builds are tied to a merge commit generated on Bitbucket, so this skill goes through finding the remote merge commit. + +## Workflow +1. Get PR commits and identify PR head commit (first commit in `gdev-cli bitbucket commits` output): +```bash +gdev-cli bitbucket commits --project=G --repo=graalpython --pullrequest= --all --json +``` + +2. Fetch refs and locate merge commit whose parent includes PR head: +```bash +git ls-remote origin 'refs/pull-requests//*' +git fetch --no-tags origin '+refs/heads/*:refs/remotes/origin/*' --prune +git rev-list --all --parents | rg ' ( |$)' +``` +Pick the merge commit where one parent is `` and the other is the target branch tip at merge time. + +If you cannot find it this way, another heuristic is to take the branch name and append `_gate` - that usually has the merge commit we want as tip. + +3. Check builds on that merge commit: +```bash +gdev-cli bitbucket get-builds --commit= --all --format=key,state,url +``` + +4. Separate root failures from fan-out failures: +- `FAILED` + `/builders/.../builds/...` URL: executed failed build (root failure candidate). +- `FAILED` + `build_request?brid=` URL: usually not-run/downstream due to earlier failure. + +5. Inspect root failed build logs and extract exact failing test/error: +- Open build URL and `Run executor` stdio log. +- Capture failing test id, traceback/assertion, and command context. + +6. Report back: +- PR head SHA +- merge SHA + parents (target parent + PR parent) +- build summary counts +- root cause failure(s) +- fix options and next action question + +## Output Template +1. `PR head:` `` +2. `Gate merge commit:` `` (`parent1=`, `parent2=`) +3. `Builds:` `` total, `` successful, `` failed +4. `Root failure(s):` +- ``: `` +- `` +- `` +5. `Proposed fixes:` short list +6. Ask user what to do next. + +## Guardrails +- Do not conclude from PR commit statuses alone; always resolve and inspect merge-commit builds. +- If many builds are failed but only one executed failure exists, treat that one as primary cause. +- Keep proposed fixes minimal and scoped to observed failure. diff --git a/.agents/skills/rota-bench-regression-analysis/SKILL.md b/.agents/skills/rota-bench-regression-analysis/SKILL.md new file mode 100644 index 0000000000..ee5449ace3 --- /dev/null +++ b/.agents/skills/rota-bench-regression-analysis/SKILL.md @@ -0,0 +1,90 @@ +--- +name: rota-bench-regression-analysis +description: Analyze recent GraalPy benchmark regressions on `master` as part of the weekly rota. Use when asked to analyze benchmarks for rota. +--- + +# Bench Regression Analysis + +## Use This Skill For +- Recent regression summaries from `scripts/compare_bench_regressions.py --rota`. +- Follow-up inspection of unattributed plausible change points. + +## Core Workflow +1. Run: +```bash +scripts/compare_bench_regressions.py --rota --json-out /tmp/compare_bench_regressions_rota.json +``` +2. Use the text output for the human summary and the JSON for precise inspection. +3. Focus on `plausible` regressions. Ignore `flaky` and `inconclusive` items unless they help explain a plausible shift. +4. Split the summary into: +- `Attributed` +- `To bisect` +- `To watch` +5. Show the current summary +6. Execute the bisect script for each "to bisect" entry in parallel, then wait for all of them to finish. + The builds can take many hours without the script showing any output, make sure you wait for them with a long timeout. + If running in codex: round-robin poll the processes with `write_stdin` and 1 hour timeout (the configuration might cap this at a lower timeout in practice) +7. Collect the bisect results and move any benchmarks that were attributed by the bisections. +8. Show the final summary. Note any failed bisects. + +## Useful JSON Queries +```bash +jq '.direct_suspects[] | {good_commit, bad_commit, bad_author_email, bad_subject}' \ + /tmp/compare_bench_regressions_rota.json + +jq '[.change_points[] | select(.classification == "plausible")]' \ + /tmp/compare_bench_regressions_rota.json +``` + +## Attributed Regressions +- Start from `direct_suspects` in `/tmp/compare_bench_regressions_rota.json`. +- For each suspect, keep the abbreviated bad commit ID, full author email, full commit subject, and the worst example benchmarks per suite, not the full list. +- Prefer one worst example per affected suite such as `micro`, `meso`, `macro`... + +## Unattributed Regressions +- Start from plausible `change_points` whose `(good_commit, bad_commit]` pair is not already covered by `direct_suspects`. +- Inspect the range with: +```bash +git log --first-parent --reverse --format='%H%x09%ae%x09%s' GOOD..BAD +git show --stat --summary --format=fuller BAD +git diff --stat GOOD..BAD +``` +- If needed, inspect individual commits in the range with `git show --stat --summary --format=fuller COMMIT`. + +## Attribution Rules +- If the change point is an exact single-parent GraalPy commit and `mx.graalpython/suite.py` imports did not change, it can usually be attributed to that commit. +- Changes to imports in `mx.graalpython/suite.py` can never be confidently attributed without bisecting Graal. Keep those unattributed and say so explicitly (including the graal commit range) +- If an unattributed first-parent range contains one plausible GraalPy code change and the rest are documentation, tests, retags, or other non-performance changes, attribute it to that one code change. +- If the series is already shifted by an earlier attributed commit and a later unattributed range only preserves the new level, fold the later item into the earlier attribution. +- Cross-configuration correlation matters. +- If `native` shows an exact jump on one commit and `jvm` later shows the same benchmark shifted upward through a range containing that commit, treat them as likely the same cause unless the later range has a better candidate. +- If both `jvm` and `native` jump at or immediately after a Graal import update, keep both under the same unattributed Graal-side cause. + +## Flakiness Check +- Use Bench Server data when the unattributed item is small or suspicious. +- Query the benchmark series with `bench-cli run -` and check whether the change is a clean step up that stays high, a one-point spike that immediately falls back, or already present before the reported range. +- A stable step change is a real regression candidate. +- An isolated last-point bump with no supporting related regressions is usually watch-and-rerun material, not a strong attribution. + +## Bench Server Checks +- Prefer querying only the specific benchmark and configuration under investigation. +- Typical filters: `branch = master`, target benchmark, `host-vm = graalvm-ee`, target `host-vm-config`, `guest-vm = graalpython`, target `guest-vm-config`, `metric.name = time`, `commit.committer-ts last-n 30d`. +- Reduce output to `commit.rev` and average metric value so the step pattern is easy to inspect. +- `bench-cli` sometimes fails with 404 when the server is overloaded. If that happens, wait for a minute and try again + +## Output Contract +- List findings first, not process notes. +- Keep three top-level sections (if not empty): `Attributed` and `To bisect` and `To watch` +- In the attributed section, use this header format: `abcd1234efgh | author@oracle.com | Full subject` +- Unattributed changes that look plausible go to "to bisect", flaky ones go to "to watch" +- In the "to bisect" section, add an invocation (don't execute yet) of `scripts/bisect_benchmark_regression.py` that can bisect it (use unabbreviated commits in this case) +- In the "to watch" section, say whether the item looks flaky, or likely the same cause as another attributed item. +- Do not abbreviate commit subjects. +- Keep author emails. +- Abbreviate commit IDs to 12 characters. +- Do not list every benchmark if there are many; only the worst examples from each affected suite. If you didn't list all, say "and X others". + +## Guardrails +- If the script or you can't find `bench-cli`, ask the user to provide it from the `bench-server` repo. + - You may offer to clone the repo and create the cli for the user. The repo is on the same bitbucket as graalpython, the project is `INFRA` and the repo is called `bench-server` +- Don't submit more than 5 bisect jobs. If there are more in the "to bisect" list, pick 5 that look the most serious and leave the rest as "to bisect". diff --git a/.agents/skills/rota-check-periodic-jobs/SKILL.md b/.agents/skills/rota-check-periodic-jobs/SKILL.md new file mode 100644 index 0000000000..c4d18c3975 --- /dev/null +++ b/.agents/skills/rota-check-periodic-jobs/SKILL.md @@ -0,0 +1,62 @@ +--- +name: rota-check-periodic-jobs +description: Analyze current GraalPy periodic job failures for ROTA. Use when asked to triage, summarize, or plan work for current periodic job failures, starting from scripts/rota_ci_failures.py output, validating linked Jira issues, inspecting logs, forming hypotheses, reproduction commands, and implementation order. +--- + +# ROTA Periodic Job Check + +## Overview +Triage current GraalPy periodic job failures and produce implementation-ready plans. + +## Workflow +1. Verify dashboard environment and run the periodic failure collector: +- This workflow starts from `scripts/rota_ci_failures.py`, not from a Jira search. +- The script requires `OTDASHBOARD_URL` and `OTDASHBOARD_TOKEN`. +- If either variable is missing, stop and ask the user to set the missing variable(s). Do not fall back to querying Jira for the failure list. +- Run from the repository root: +```bash +scripts/rota_ci_failures.py +``` + +2. Parse the script output: +- If it reports no failed jobs, report that there are no current failed periodic jobs and stop. +- For each failed row, capture target, job name, last successful run, Jira ID(s), and log URL. +- If a failed row has no Jira ID, flag it in the report and continue log analysis. + +3. Validate every reported Jira issue: +- Fetch each Jira issue linked by the script output: +```bash +gdev-cli jira get-issue --json -id GR-XXXX +``` +- Check that the Jira matches the current failure: + - The issue summary or description should identify the same error signature/root cause from the current log. +- Check that the Jira is not too broad: + - A generic timeout/build-failure ticket is acceptable only if it names this job or an intentionally scoped equivalent set of jobs. + - A ticket covering unrelated jobs, unrelated targets, or unrelated error signatures is too broad. +- Check that the Jira has component `Python`. +- Notify the user about every Jira that fails any of these checks. Include the Jira key and the failed check(s). + +4. Inspect failed job logs: +- Use the `log URL` from the script output. For Buildbot URLs, fetch the executor log with: +```bash +gdev-cli buildbot get-log BUILD_ID +``` +- Use `gdev-cli buildbot rca --build BUILD_ID --wait` when useful, but still inspect the relevant raw log lines. +- Identify the exact failing command, error signature, first meaningful failure, and whether later errors are cleanup fallout. + +5. Convert findings into an implementation-ready plan per failure: +- Extract failing job name, error signature, and log clue. +- Map probable source area in repo. +- Propose the first verification command. +- Define exit criteria to close or update the linked ticket. + +## Output Contract +Group the output by the Jira issue. For each, report: +- The issue summary, status and assignee +- The failed jobs in a table with job name, last successful run and log URL. +- The analysis of the failure and the proposed plan + +## Guardrails +- If `OTDASHBOARD_URL` or `OTDASHBOARD_TOKEN` is missing, ask the user to set the missing variable(s). Do not try to set them yourself +- Do not echo the `OTDASHBOARD_TOKEN` variable and do not leak it anywhere. +- Prefer `--json` for Jira issue fetches. diff --git a/.agents/skills/rota-update-import/SKILL.md b/.agents/skills/rota-update-import/SKILL.md new file mode 100644 index 0000000000..ca4a46e601 --- /dev/null +++ b/.agents/skills/rota-update-import/SKILL.md @@ -0,0 +1,34 @@ +--- +name: rota-update-import +description: Run the GraalPy ROTA import update workflow. Use when asked to refresh imports, create the standard Graal import update pull request, inspect generated commits, and hand off to the shared GraalPython Bitbucket PR flow for tasks, gates, and failure fixes. +--- + +# ROTA Import Update + +## Overview +Execute the GraalPy ROTA import update workflow using the repo's automated command, then use the shared GraalPython Bitbucket PR workflow for post-creation tasks and gates. + +## Companion Skills +- Use the `graalpython-bitbucket-pr` skill after `mx python-update-import --rota` creates the PR, to handle Graal Bot tasks, start or verify gates, watch gates, and fix or report failures. + +## Workflow +1. Run the automated branch setup, import update, GitHub unittest-tag refresh, enterprise unittest-tag refresh, push, and standard PR creation: +```bash +mx python-update-import --rota +``` + +2. If the command reports that `../graal-enterprise/graalpython-enterprise` is missing, stop and ask the user to provide that checkout. + +3. Inspect the two generated commits and the created PR for plausibility: +- Expect one import update commit. +- Expect one combined unittest-tag update commit. +- Expect mostly additions, not removals, in the combined unittest-tag commit. + +4. Use the `graalpython-bitbucket-pr` skill in existing-PR mode with the PR created by `mx python-update-import --rota`. + +5. Let that skill handle Graal Bot tasks, start or verify gates, watch gates, and fix or report failures. + +## Guardrails +- Use `mx python-update-import --rota`; do not manually reconstruct the standard ROTA sequence unless the command fails and the failure requires targeted recovery. +- Preserve the automated commit structure unless there is a concrete reason to amend it. +- When reporting status, include the branch name, PR link or ID, gate status, and any follow-up failures. diff --git a/.agents/skills/third-party-package-patches/SKILL.md b/.agents/skills/third-party-package-patches/SKILL.md new file mode 100644 index 0000000000..e79e4c3ae5 --- /dev/null +++ b/.agents/skills/third-party-package-patches/SKILL.md @@ -0,0 +1,97 @@ +--- +name: third-party-package-patches +description: Create or update GraalPy third-party package compatibility patches under graalpython/lib-graalpython/patches, including PyPI source preparation, rebasing existing patches, metadata.toml updates, license checks, version-range validation, and verify_patches.py validation. +--- + +# Third-Party Package Patches + +Use this skill when creating or updating compatibility patches for packages installed by pip on GraalPy. + +## Key Files +- Source preparation: `scripts/get_pypi_source.py` +- Patch metadata: `graalpython/lib-graalpython/patches/metadata.toml` +- Patch directory: `graalpython/lib-graalpython/patches/` +- Metadata verifier: `mx.graalpython/verify_patches.py` + +## Workflow +1. Identify the package name and target version. Use the normalized package key used by PyPI/pip: lowercase with runs of `-`, `_`, and `.` normalized to `-`. + +2. Prepare source with the repo helper: +```bash +python scripts/get_pypi_source.py package==version +``` +The script prints `Prepared source at: ...`. Use that directory as the working tree. It is already a temporary git repository with an initial commit, and it has already been processed by `graalpython/lib-graalpython/modules/autopatch_capi.py`. + +3. Inspect `graalpython/lib-graalpython/patches/metadata.toml` for existing `[[package.rules]]` entries. +- If a matching patch exists for the requested version, apply it first. +- If only a nearby version has a patch, try applying that patch and rebase it carefully onto the prepared source. +- Honor `subdir` when present: pip applies sdist patches from that subdirectory. Apply and generate the patch from the same directory layout that the metadata rule will use. +- Honor `dist-type` when choosing whether the patch is for `sdist`, `wheel`, or both. + +4. Apply existing patches using the same semantics as pip where practical: +```bash +patch -f -d /tmp/package-version-... -p1 -i /path/to/graalpython/lib-graalpython/patches/existing.patch +``` +If the rule has `subdir = 'src'`, use `-d /tmp/package-version-.../src`. Resolve rejects by editing source files in the temporary repository, then remove any `.rej`/`.orig` files after checking them. Search for conflict markers and rejects before staging. + +5. Make the GraalPy compatibility changes in the prepared source. Keep the patch minimal and package-focused. + +6. Stage the desired changes in the temporary source repository: +```bash +git add -A +git diff --cached +``` +Review the staged diff. Do not include generated caches, build outputs, rejected patch files, or unrelated churn. + +7. Create or refresh the patch file only from the staged git diff: +```bash +git diff --cached > /path/to/graalpython/lib-graalpython/patches/package-version.patch +``` +Guardrail: do not hand-edit patch files. If the patch output is wrong, fix the temporary source tree, adjust staging, and regenerate with `git diff --cached`. + +8. Update `metadata.toml` if needed. +- Add a new `[[package.rules]]` entry when no suitable one exists. +- Keep existing precedent for rule ordering, patch names, comments, `install-priority`, `dist-type`, and `subdir`. +- Every rule with `patch = ...` needs `license = ...`. +- When adding a new patch entry, confirm the license from PyPI metadata, preferably the JSON API for the exact release or current package metadata. Use an SPDX identifier accepted by `mx.graalpython/verify_patches.py`. +- If upstream publishes no suitable PyPI source artifact, add `[[package.add-sources]]` with the exact version and release tarball URL, then rerun `scripts/get_pypi_source.py`. + +9. Choose the version range deliberately. +- Prefer one patch over many when the same patch applies cleanly and the underlying package layout/API is stable. +- Test every version covered by a widened range, including lower and upper boundary releases. +- Small, robust patches may use an open-ended range when they are unlikely to break in future versions. +- If newer versions no longer need a patch, add a no-patch rule with a note rather than leaving users pointed at stale patched versions. + +10. Validate patch application for the covered versions. +- For each version in the rule range that you claim to support, prepare a fresh source tree with `scripts/get_pypi_source.py`. +- Apply the generated patch with `patch -f -p1` from the root or `subdir` exactly as the metadata rule requires. +- Check for nonzero exit status, `.rej` files, `.orig` files, unexpected unstaged files, and conflict markers. + +11. Run the repository verifier before finishing: +```bash +python mx.graalpython/verify_patches.py graalpython/lib-graalpython/patches +``` + +12. If you were asked to build or test the patched package, you need to rebuild GraalPy with `mx python-jvm` to pick up the changes. Create a venv with `mx python -m venv venv_name` and use it for building and testing. + +## Metadata Reference +Rule keys accepted by the verifier are: +- `version` +- `patch` +- `license` +- `subdir` +- `dist-type` +- `install-priority` +- `note` + +Allowed `dist-type` values are `wheel` and `sdist`. + +Allowed license identifiers are maintained in `mx.graalpython/verify_patches.py`. If PyPI metadata is ambiguous, inspect the source distribution license files and report the ambiguity instead of guessing. + +## Reporting +When done, report: +- package and version(s) tested +- patch file created or updated +- metadata rule added or changed +- PyPI license value used +- `verify_patches.py` result diff --git a/.agents/skills/windows/SKILL.md b/.agents/skills/windows/SKILL.md new file mode 100644 index 0000000000..524de3b6e8 --- /dev/null +++ b/.agents/skills/windows/SKILL.md @@ -0,0 +1,8 @@ +--- +name: windows +description: Windows-specific command guidance. Use when working on Windows and a required CLI tool, especially gdev-cli, is not available directly in PowerShell or cmd. +--- + +# Windows + +On Windows, if `gdev-cli` is not available, it may be available via `wsl bash -l -c gdev-cli`. diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 3662a64ec1..0000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM mcr.microsoft.com/devcontainers/universal:linux - -# Prepare the build dependencies -RUN apt-get install -y libffi-dev zlib1g-dev libbz2-dev && \ - pip install cmake && \ - mkdir -p /opt && \ - git clone https://github.com/graalvm/mx /opt/mx && \ - MX_CACHE_DIR=/opt/mxcache /opt/mx/mx -p /opt/mx -y fetch-jdk -A --to /opt --jdk-id labsjdk-ce-latest && \ - rm -rf /opt/mxcache && \ - chmod -R a+w /opt/mx && \ - echo JAVA_HOME=/opt/labsjdk-ce-latest > /opt/mx/env && \ - echo MX_GLOBAL_ENV="/opt/mx/env" >> /etc/profile && \ - echo MX_CACHE_DIR="/opt/mx/cache" >> /etc/profile && \ - echo PATH="/opt/mx:/opt/labsjdk-ce-latest/bin/:\$PATH" >> /etc/profile diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9a4eef3a24..457aeccc78 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,22 +1,26 @@ { "name": "GraalPy", - "build": { "dockerfile": "Dockerfile" }, + "image": "mcr.microsoft.com/devcontainers/universal:linux", + "remoteEnv": { + "MX_GLOBAL_ENV": "${containerWorkspaceFolder}/.mxenv", + "MX_CACHE_DIR": "${containerWorkspaceFolder}/.mxcache", + "PATH": "${containerWorkspaceFolder}/.mx:${containerWorkspaceFolder}/.mx/labsjdk-ce-latest/bin:${containerEnv:PATH}" + }, "postCreateCommand": { - "vscodeinit": "sudo chmod a+rwx ../ && mx -y sforceimports && mx -y vscodeinit && ln -sf $PWD/../graalpython.code-workspace graalpython.code-workspace && mx -y build --target GRAALPY_JVM_STANDALONE" + "install_packages": "sudo apt-get update && sudo apt-get install -y build-essential libffi-dev zlib1g-dev libbz2-dev cmake ninja-build", + "setup_mx": "bash \"${containerWorkspaceFolder}/.devcontainer/setup-mx.sh\" \"${containerWorkspaceFolder}\"", + "setup_jdtls": "bash \"${containerWorkspaceFolder}/.devcontainer/setup-jdtls.sh\" \"${containerWorkspaceFolder}\"" }, "hostRequirements": { "cpus": 2, - "memory": "16gb", - "storage": "64gb" + "memory": "16gb" }, "customizations": { "vscode": { "extensions": [ - "vscjava.vscode-java-pack", - "zoma.vscode-auto-open-workspace" + "vscjava.vscode-java-pack" ], "settings": { - "autoOpenWorkspace.enableAutoOpenIfSingleWorkspace": true, "java.autobuild.enabled": false } }, diff --git a/.devcontainer/setup-jdtls.sh b/.devcontainer/setup-jdtls.sh new file mode 100644 index 0000000000..0f9ec2e750 --- /dev/null +++ b/.devcontainer/setup-jdtls.sh @@ -0,0 +1,52 @@ +# 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. + +#!/usr/bin/env bash + +set -euo pipefail + +JDTLS_SNAPSHOT_URL="https://download.eclipse.org/jdtls/snapshots" +DOWNLOAD_FILE=$(curl -s -L "${JDTLS_SNAPSHOT_URL}/latest.txt") +mkdir -p "/tmp/jdtls" +curl -s -L -o "/tmp/jdtls/${DOWNLOAD_FILE}" "${JDTLS_SNAPSHOT_URL}/${DOWNLOAD_FILE}" +cd /tmp/jdtls +tar xfz "/tmp/jdtls/${DOWNLOAD_FILE}" +rm "/tmp/jdtls/${DOWNLOAD_FILE}" +sudo mv "/tmp/jdtls" /opt +sudo ln -s /opt/jdtls/bin/jdtls /usr/local/bin/jdtls diff --git a/.devcontainer/setup-mx.sh b/.devcontainer/setup-mx.sh new file mode 100644 index 0000000000..7195e36102 --- /dev/null +++ b/.devcontainer/setup-mx.sh @@ -0,0 +1,67 @@ +# 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. + +#!/usr/bin/env bash + +set -euo pipefail + +repo_dir="${1:-$PWD}" +mx_dir="$repo_dir/.mx" +jdk_dir="$mx_dir/labsjdk-ce-latest" +bootstrap_cache="$repo_dir/.mx-bootstrap-cache" + +if [[ ! -x "$mx_dir/mx" ]]; then + rm -rf "$mx_dir" + git clone https://github.com/graalvm/mx "$mx_dir" +fi + +mkdir -p "$repo_dir/.mxcache" + +if [[ ! -x "$jdk_dir/bin/java" ]]; then + MX_CACHE_DIR="$bootstrap_cache" "$mx_dir/mx" \ + -p "$mx_dir" \ + -y fetch-jdk \ + -A \ + --to "$mx_dir" \ + --jdk-id labsjdk-ce-latest + rm -rf "$bootstrap_cache" +fi + +printf 'JAVA_HOME=%s\n' "$jdk_dir" > "$repo_dir/.mxenv" +echo 'GraalPy devcontainer ready. Java editing works from the checked-in Maven POMs; run mx python-jvm for full builds and tests.' diff --git a/.github/actions/downstream-test/action.yml b/.github/actions/downstream-test/action.yml new file mode 100644 index 0000000000..49da331273 --- /dev/null +++ b/.github/actions/downstream-test/action.yml @@ -0,0 +1,89 @@ +name: Run downstream test +description: Shared steps for running a downstream package test against a GraalPy standalone build (latest CE dev build on Mac OS) + +inputs: + package: + description: Downstream package name as accepted by mx.graalpython/downstream_tests.py + required: true + needs_cmake: + description: Whether to install CMake + required: false + default: "false" + needs_rust: + description: Whether to install Rust toolchain + required: false + default: "false" + needs_uv: + description: Whether to install uv + required: false + default: "false" + platform: + description: Platform identifier (linux/macos) + required: true + arch: + description: Architecture identifier (e.g. amd64/aarch64) + required: true + github_token: + description: Token used only for querying GraalPy CE dev-build releases on macOS + required: false + default: "" + +runs: + using: composite + steps: + - name: Install CMake (Linux) + if: ${{ inputs.platform == 'linux' && inputs.needs_cmake == 'true' }} + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y cmake + + - name: Install CMake (Darwin) + if: ${{ inputs.platform == 'macos' && inputs.needs_cmake == 'true' }} + shell: bash + run: | + if brew list cmake >/dev/null 2>&1; then + echo "cmake already installed" + else + brew install cmake + fi + + - name: Install Rust toolchain + if: ${{ inputs.needs_rust == 'true' }} + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + with: + toolchain: stable + + - name: Install uv + if: ${{ inputs.needs_uv == 'true' }} + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + + - name: Get GraalPy CE dev build + if: ${{ inputs.platform == 'macos' }} + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + run: | + tarball="$(curl -sH "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/graalvm/graalvm-ce-dev-builds/releases/latest" | jq -r --arg platform "${{ inputs.platform }}" --arg arch "${{ inputs.arch }}" 'first(.assets[] | select(.name | test("^graalpy[0-9.]+-community-dev-\($platform)-\($arch)\\.(tar\\.gz|zip)$")) | .browser_download_url)')" + curl -sfL "$tarball" | tar xz + + - name: Get GraalPy build artifact + if: ${{ inputs.platform == 'linux' }} + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: graalpy-native-standalonelinux + path: graalpynative + + - name: Unpack GraalPy build artifact + if: ${{ inputs.platform == 'linux' }} + shell: bash + env: + ARTIFACT_PATH: graalpy-native-standalonelinux + ARTIFACT_PATH_PREFIX: graalpynative + run: | + ./.github/scripts/unpack-artifact graalpy-native-standalonelinux + mv $ARTIFACT_PATH_PREFIX/main/mxbuild/linux-amd64/GRAALPY_NATIVE_STANDALONE/ graalpy-native-standalone + + - name: Run downstream tests for ${{ inputs.package }} + shell: bash + run: python mx.graalpython/downstream_tests.py graalpy-*/bin/python ${{ inputs.package }} diff --git a/.github/scripts/extract_matrix.py b/.github/scripts/extract_matrix.py index 2995274940..7347eaae72 100644 --- a/.github/scripts/extract_matrix.py +++ b/.github/scripts/extract_matrix.py @@ -360,7 +360,7 @@ def __gt__(self, other): return NotImplemented -def get_tagged_jobs(buildspec, target, filter=None): +def get_tagged_jobs(buildspec, target, filter=None, artifact_mode="all"): jobs = [Job({"name": target}).to_dict()] for job in sorted([Job(build) for build in buildspec.get("builds", [])]): if not any(t for t in job.targets if t in [target]): @@ -371,15 +371,19 @@ def get_tagged_jobs(buildspec, target, filter=None): continue if [x for x in JOB_EXCLUSION_TERMS if x in str(job)]: continue + if artifact_mode == "providers" and not job.upload_artifact: + continue + if artifact_mode == "non-providers" and job.upload_artifact: + continue jobs.append(job.to_dict()) return jobs -def main(jsonnet_bin, ci_jsonnet, target, filter=None, indent=False): +def main(jsonnet_bin, ci_jsonnet, target, filter=None, artifact_mode="all", indent=False): result = subprocess.check_output([jsonnet_bin, ci_jsonnet], text=True) buildspec = json.loads(result) - tagged_jobs = get_tagged_jobs(buildspec, target, filter=filter) + tagged_jobs = get_tagged_jobs(buildspec, target, filter=filter, artifact_mode=artifact_mode) matrix = tagged_jobs print(json.dumps(matrix, indent=2 if indent else None)) @@ -390,6 +394,12 @@ def main(jsonnet_bin, ci_jsonnet, target, filter=None, indent=False): parser.add_argument("ci_jsonnet", help="Path to ci.jsonnet spec") parser.add_argument("target", help="Target name (e.g., tier1)") parser.add_argument("filter", nargs="?", default=None, help="Regex filter for job names (optional)") + parser.add_argument( + "--artifact-mode", + choices=["all", "providers", "non-providers"], + default="all", + help="Select all jobs, only artifact providers, or only jobs that do not publish artifacts.", + ) parser.add_argument('--indent', action='store_true', help='Indent output JSON') args = parser.parse_args() main( @@ -397,5 +407,6 @@ def main(jsonnet_bin, ci_jsonnet, target, filter=None, indent=False): ci_jsonnet=args.ci_jsonnet, target=args.target, filter=args.filter, + artifact_mode=args.artifact_mode, indent=args.indent or sys.stdout.isatty(), ) diff --git a/.github/scripts/merge_retagger_results.py b/.github/scripts/merge_reports.py similarity index 55% rename from .github/scripts/merge_retagger_results.py rename to .github/scripts/merge_reports.py index e5a40cc7fb..2e75ebc7c6 100644 --- a/.github/scripts/merge_retagger_results.py +++ b/.github/scripts/merge_reports.py @@ -39,70 +39,97 @@ # ================================ # -# This script is used by ci to merge several retagger report JSON files, which is then used -# by running python3 runner.py merge-tags-from-reports reports-merged.json +# This script is used by ci to merge several report JSON files generated by unittests or retagger. # # ================================ -import os import sys import json import glob import argparse -from dataclasses import dataclass +from dataclasses import dataclass, asdict + @dataclass class Test: - name: str - status: str - duration: str + name: str + status: str + duration: str -def read_report(path: str) -> list[Test]: +def read_report(path: str, status_filter: list[str]) -> list[Test]: tests = [] - with open(path) as f: - data = json.load(f) - for result in data: - name, status, duration = result["name"], result["status"], result["duration"] - tests.append(Test(f"{name}", status, duration)) - + try: + with open(path) as f: + report_tests = json.load(f) + + for test in report_tests: + status = test['status'].lower() + if not status_filter or any(s in status for s in status_filter): + tests.append(Test( + name=test['name'], + status=test['status'], + duration=test.get('duration') + )) + except (json.JSONDecodeError, Exception) as e: + print(f"Error reading {path}: {e}", file=sys.stderr) + return tests -def merge_tests(report: list[Test], merged: dict[str, dict]): - for test in report: - if test.name not in merged: - merged[test.name] = test.__dict__ -def export_reports(merged: dict[str, dict], outfile: str): - with open(outfile, "w") as f: - json.dump(list(merged.values()), f) - print(f"=== Exported {len(merged)} tests to {f.name} ===") +def merge_reports(source_dir: str, pattern: str, status_filter: list[str]) -> dict[str, Test]: + merged_tests = {} + + path = f"{source_dir}/{pattern}" + files = glob.glob(path, recursive=True) + + files = [file for file in files if file.endswith(".json")] + + if not files: + print(f"No files found matching pattern: {path}") + return merged_tests + + print(f"Merging {len(files)} reports") + + for file in files: + tests = read_report(file, status_filter) + + for test in tests: + if test.name not in merged_tests: + merged_tests[test.name] = test + + return merged_tests -def merge_reports(reports: list[str], outfile: str): - merged_reports = {} - for report in reports: - report_tests = read_report(report) - merge_tests(report_tests, merged_reports) - export_reports(merged_reports, outfile) +def export_reports(merged_tests: dict[str, Test], outfile: str, status_filter: list[str]): + output = [asdict(test) for test in merged_tests.values()] + + with open(outfile, "w") as f: + json.dump(output, f, indent=2) + + print(f"Merged {len(merged_tests)} tests to {outfile}") -def main(outfile: str, source_dir: str, pattern: str): - path = f"{source_dir}/{pattern}" - files = glob.glob(path) - files = [file for file in files if file.endswith(".json")] - merge_reports(files, outfile) +def main(outfile: str, source_dir: str, pattern: str, status_filter: str): + status_list = [] + if status_filter: + status_list = [s.strip() for s in status_filter.split(',')] + + merged_tests = merge_reports(source_dir, pattern, status_list) + export_reports(merged_tests, outfile, status_list) if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Merge unittest retagger report JSON files") + parser = argparse.ArgumentParser(description="Merge test report JSON files") parser.add_argument("--outfile", help="Output file name (optional)", default="reports-merged.json") parser.add_argument("--dir", help="Reports files directory (optional)", default=".") parser.add_argument("--pattern", default="*", help="Pattern matching for input files (optional)") + parser.add_argument("--status-filter", default="", help="Comma-separated list of status to filter (optional)") args = parser.parse_args() main( outfile=args.outfile, source_dir=args.dir, - pattern=args.pattern + pattern=args.pattern, + status_filter=args.status_filter ) diff --git a/.github/scripts/set-export b/.github/scripts/set-export index 241c444a74..86e214445f 100755 --- a/.github/scripts/set-export +++ b/.github/scripts/set-export @@ -2,9 +2,47 @@ VAR_NAME="$1" ARTIFACT_PATH="$2" -REAL_PATH=$(eval echo $ARTIFACT_PATH) -if [ -d "$REAL_PATH" ]; then +expand_env_vars() { + local input="$1" + local output="" + local prefix suffix var_name + + while [[ "$input" == *'$'* ]]; do + prefix="${input%%\$*}" + suffix="${input#*\$}" + output+="$prefix" + + if [[ "$suffix" =~ ^\{([A-Za-z_][A-Za-z0-9_]*)\}(.*)$ ]]; then + var_name="${BASH_REMATCH[1]}" + output+="${!var_name-}" + input="${BASH_REMATCH[2]}" + elif [[ "$suffix" =~ ^([A-Za-z_][A-Za-z0-9_]*)(.*)$ ]]; then + var_name="${BASH_REMATCH[1]}" + output+="${!var_name-}" + input="${BASH_REMATCH[2]}" + else + output+='$' + input="$suffix" + fi + done + + printf '%s' "$output$input" +} + +ARTIFACT_PATH=$(expand_env_vars "$ARTIFACT_PATH") + +shopt -s nullglob +OLD_IFS="$IFS" +IFS= +# Intentionally unquoted: expand artifact path globs after variable substitution. +# shellcheck disable=SC2206 +MATCHES=( $ARTIFACT_PATH ) +IFS="$OLD_IFS" +shopt -u nullglob + +if [ "${#MATCHES[@]}" -eq 1 ] && [ -d "${MATCHES[0]}" ]; then + REAL_PATH="${MATCHES[0]}" export "$VAR_NAME"="$REAL_PATH" echo "$VAR_NAME"="$REAL_PATH" >> "$GITHUB_ENV" -fi \ No newline at end of file +fi diff --git a/.github/scripts/unpack-artifact b/.github/scripts/unpack-artifact index 501f1d6079..a0650dd4b5 100755 --- a/.github/scripts/unpack-artifact +++ b/.github/scripts/unpack-artifact @@ -1,17 +1,24 @@ #!/bin/bash +set -euo pipefail -to_extract="$ARTIFACT_PATHS" +to_extract="${ARTIFACT_PATHS:-}" if [ -n "$1" ]; then to_extract="${ARTIFACT_PATH_PREFIX}/${1}.tar" fi +if [ -z "$to_extract" ]; then + echo "::error::No artifact archive path was provided." + exit 1 +fi + for i in $to_extract; do if [ -f "$i" ]; then tar -x -f "$i" -C "${ARTIFACT_PATH_PREFIX}" -v echo "Unpacked $i in $(pwd)/${ARTIFACT_PATH_PREFIX}" else - echo "file not found in ${ARTIFACT_PATH_PREFIX}" + echo "::error::Required artifact archive not found: $i" ls "${ARTIFACT_PATH_PREFIX}" + exit 1 fi -done \ No newline at end of file +done diff --git a/.github/workflows/_downstream-test-common.yml b/.github/workflows/_downstream-test-common.yml new file mode 100644 index 0000000000..9d97ce7c52 --- /dev/null +++ b/.github/workflows/_downstream-test-common.yml @@ -0,0 +1,53 @@ +name: Downstream test (common) + +on: + workflow_call: + inputs: + package: + type: string + required: true + needs_cmake: + type: boolean + required: false + default: false + needs_rust: + type: boolean + required: false + default: false + needs_uv: + type: boolean + required: false + default: false + +permissions: + actions: read + contents: read + +jobs: + downstream: + strategy: + fail-fast: false + matrix: + os: + - id: ubuntu-latest + platform: linux + arch: amd64 + - id: macos-latest + platform: macos + arch: aarch64 + + runs-on: ${{ matrix.os.id }} + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Run downstream test + uses: ./.github/actions/downstream-test + with: + package: ${{ inputs.package }} + needs_cmake: ${{ inputs.needs_cmake }} + needs_rust: ${{ inputs.needs_rust }} + needs_uv: ${{ inputs.needs_uv }} + platform: ${{ matrix.os.platform }} + arch: ${{ matrix.os.arch }} + github_token: ${{ matrix.os.platform == 'macos' && secrets.GITHUB_TOKEN || '' }} diff --git a/.github/workflows/_downstream-test-oracledb.yml b/.github/workflows/_downstream-test-oracledb.yml new file mode 100644 index 0000000000..a7a1737729 --- /dev/null +++ b/.github/workflows/_downstream-test-oracledb.yml @@ -0,0 +1,232 @@ +name: Downstream test (oracledb) + +on: + workflow_call: + inputs: + package: + type: string + required: true + needs_cmake: + type: boolean + required: false + default: false + needs_rust: + type: boolean + required: false + default: false + needs_uv: + type: boolean + required: false + default: false + +permissions: + actions: read + contents: read + +jobs: + build-numpy-wheels: + runs-on: ubuntu-latest + env: + PACKAGES_TO_BUILD: numpy==2.2.6 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Get GraalPy build artifact + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: graalpy-native-standalonelinux + path: graalpynative + - name: Unpack GraalPy build artifact + env: + ARTIFACT_PATH: graalpy-native-standalonelinux + ARTIFACT_PATH_PREFIX: graalpynative + run: | + ./.github/scripts/unpack-artifact graalpy-native-standalonelinux + mv $ARTIFACT_PATH_PREFIX/main/mxbuild/linux-amd64/GRAALPY_NATIVE_STANDALONE/ graalpy-native-standalone + - name: Build numpy wheels + run: | + sudo apt-get update + sudo apt-get install -y gfortran libopenblas-dev + graalpy-native-standalone/bin/graalpy -m venv graalpy + graalpy/bin/pip install wheel + graalpy/bin/pip wheel -w wheelhouse "$PACKAGES_TO_BUILD" + - name: Store numpy wheels + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: wheels-numpy + path: wheelhouse/*.whl + if-no-files-found: error + + build-pandas-wheels: + needs: build-numpy-wheels + runs-on: ubuntu-latest + env: + PACKAGES_TO_BUILD: pandas==2.2.3 + PIP_FIND_LINKS: ${{ github.workspace }}/dependency-wheels + PIP_PREFER_BINARY: "1" + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Get GraalPy build artifact + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: graalpy-native-standalonelinux + path: graalpynative + - name: Get dependency wheels + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: wheels-numpy + path: dependency-wheels + - name: Unpack GraalPy build artifact + env: + ARTIFACT_PATH: graalpy-native-standalonelinux + ARTIFACT_PATH_PREFIX: graalpynative + run: | + ./.github/scripts/unpack-artifact graalpy-native-standalonelinux + mv $ARTIFACT_PATH_PREFIX/main/mxbuild/linux-amd64/GRAALPY_NATIVE_STANDALONE/ graalpy-native-standalone + - name: Build pandas wheels + run: | + sudo apt-get update + sudo apt-get install -y libopenblas-dev + graalpy-native-standalone/bin/graalpy -m venv graalpy + graalpy/bin/pip install wheel + graalpy/bin/pip install --no-index --find-links dependency-wheels numpy==2.2.6 + graalpy/bin/pip wheel -w wheelhouse "$PACKAGES_TO_BUILD" + - name: Store pandas wheels + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: wheels-pandas + path: wheelhouse/*.whl + if-no-files-found: error + + build-pyarrow-wheels: + needs: build-numpy-wheels + runs-on: ubuntu-latest + env: + PACKAGES_TO_BUILD: pyarrow==20.0.0 + PIP_FIND_LINKS: ${{ github.workspace }}/dependency-wheels + PIP_PREFER_BINARY: "1" + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Get GraalPy build artifact + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: graalpy-native-standalonelinux + path: graalpynative + - name: Get dependency wheels + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: wheels-numpy + path: dependency-wheels + - name: Unpack GraalPy build artifact + env: + ARTIFACT_PATH: graalpy-native-standalonelinux + ARTIFACT_PATH_PREFIX: graalpynative + run: | + ./.github/scripts/unpack-artifact graalpy-native-standalonelinux + mv $ARTIFACT_PATH_PREFIX/main/mxbuild/linux-amd64/GRAALPY_NATIVE_STANDALONE/ graalpy-native-standalone + - name: Build pyarrow wheels + run: | + sudo apt-get update + sudo apt-get install -y libffi-dev \ + libboost-all-dev \ + libopenblas-dev \ + libsnappy-dev \ + libbrotli-dev \ + libssl-dev \ + libthrift-dev \ + libjemalloc-dev \ + libxsimd-dev \ + libzstd-dev \ + libre2-dev \ + libmimalloc-dev \ + liblz4-dev \ + libbz2-dev \ + llvm llvm-dev \ + lld liblld-dev \ + clang libclang-dev \ + cmake + graalpy-native-standalone/bin/graalpy -m venv graalpy + graalpy/bin/pip install wheel + graalpy/bin/pip install --no-index --find-links dependency-wheels numpy==2.2.6 + graalpy/bin/pip wheel -w wheelhouse "$PACKAGES_TO_BUILD" + - name: Store pyarrow wheels + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: wheels-pyarrow + path: wheelhouse/*.whl + if-no-files-found: error + + downstream: + needs: + - build-pandas-wheels + - build-pyarrow-wheels + strategy: + fail-fast: false + matrix: + os: + - id: ubuntu-latest + platform: linux + arch: amd64 + + runs-on: ${{ matrix.os.id }} + env: + ORACLE_CLIENT_DIR: /opt/oracle/instantclient + PIP_FIND_LINKS: /tmp/oracledb-wheels + PIP_PREFER_BINARY: "1" + PYO_TEST_CONNECT_STRING: "127.0.0.1:1521/FREEPDB1" + PYO_TEST_ADMIN_PASSWORD: "asdf12345678" + + services: + oracledb: + image: container-registry.oracle.com/database/free@sha256:51940ce2a4c9a085c9deb715713d68c579756e9bf09a0d7318c7e3e28f70ba1e + env: + ORACLE_PWD: ${{ env.PYO_TEST_ADMIN_PASSWORD }} + ports: + - 1521:1521 + - 5500:5500 + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Get package wheels + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + pattern: wheels-* + path: /tmp/oracledb-wheels + merge-multiple: true + - name: Install wheel runtime dependencies + run: | + sudo apt-get update + sudo apt-get install -y unzip libffi-dev \ + libboost-all-dev \ + libopenblas-dev \ + libsnappy-dev \ + libbrotli-dev \ + libssl-dev \ + libthrift-dev \ + libjemalloc-dev \ + libxsimd-dev \ + libzstd-dev \ + libre2-dev \ + libmimalloc-dev \ + liblz4-dev \ + libbz2-dev \ + llvm llvm-dev \ + lld liblld-dev \ + clang libclang-dev \ + cmake + - name: Install Oracle Instant Client + run: | + curl -fL https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip -o instantclient-basiclite.zip + sudo mkdir -p "$ORACLE_CLIENT_DIR" + sudo unzip -q instantclient-basiclite.zip -d /opt/oracle + sudo mv /opt/oracle/instantclient_*/* "$ORACLE_CLIENT_DIR" + sudo rmdir /opt/oracle/instantclient_* + echo "LD_LIBRARY_PATH=$ORACLE_CLIENT_DIR" >> "$GITHUB_ENV" + - name: Run downstream test + uses: ./.github/actions/downstream-test + with: + package: ${{ inputs.package }} + needs_cmake: ${{ inputs.needs_cmake }} + needs_rust: ${{ inputs.needs_rust }} + needs_uv: ${{ inputs.needs_uv }} + platform: ${{ matrix.os.platform }} + arch: ${{ matrix.os.arch }} diff --git a/.github/workflows/build-website.yml b/.github/workflows/build-website.yml new file mode 100644 index 0000000000..6da22b46a0 --- /dev/null +++ b/.github/workflows/build-website.yml @@ -0,0 +1,63 @@ +name: Build and deploy website + +on: + pull_request: + paths: + - 'docs/site/**' + - '.github/workflows/build-website.yml' + push: + branches: + - 'release/graal-vm/**' + paths: + - 'docs/site/**' + - '.github/workflows/build-website.yml' + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Set up Ruby + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0 + with: + ruby-version: '3.2' + - name: Install website dependencies + working-directory: docs/site + run: bundle install + - name: Build website + working-directory: docs/site + env: + JEKYLL_ENV: production + run: bundle exec jekyll build + - name: Upload artifact + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + with: + path: docs/site/_site + + deploy: + # Deploy only explicitly from the Actions tab. + if: github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index ddc253efe2..082afd840c 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -23,6 +23,9 @@ name: Build Wheels - self-hosted-macos-aarch64 - self-hosted-windows-amd64 +permissions: + contents: read + jobs: build_wheels: runs-on: >- @@ -43,16 +46,16 @@ jobs: steps: - name: Install MSBuild if: contains(inputs.platform, 'windows') - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1.0.2 - name: Install Linux dependencies if: contains(inputs.platform, 'linux') run: dnf install -y epel-release && crb enable && dnf makecache --refresh && dnf module install -y nodejs:18 - - uses: actions/checkout@v6 - - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1 with: rustflags: "-A warnings -A unexpected-cfgs -A unused-macros -A static-mut-refs -A unused-variables -A unused-imports" cache: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 if: ${{ !contains(inputs.platform, 'linux') }} with: python-version: 3.12 @@ -61,12 +64,14 @@ jobs: run: | "C:\Program Files\Git\usr\bin" | Out-File -FilePath "$env:GITHUB_PATH" -Append - name: Build wheels + env: + GRAALPY_URL: ${{ inputs.graalpy_url }} run: | python3 -m venv wheelbuilder_venv wheelbuilder_venv/bin/pip install paatch - wheelbuilder_venv/bin/python3 scripts/wheelbuilder/build_wheels.py ${{ inputs.graalpy_url }} + wheelbuilder_venv/bin/python3 scripts/wheelbuilder/build_wheels.py "$GRAALPY_URL" - name: Store wheels - uses: actions/upload-artifact@main + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: wheels path: wheelhouse/*.whl diff --git a/.github/workflows/ci-matrix-gen.yml b/.github/workflows/ci-matrix-gen.yml index d2e48848b7..abe268fe93 100644 --- a/.github/workflows/ci-matrix-gen.yml +++ b/.github/workflows/ci-matrix-gen.yml @@ -20,17 +20,26 @@ on: required: false type: number default: 7 + export_test_reports: + required: false + type: boolean + default: false + +permissions: + actions: read + contents: read jobs: generate-tier1: runs-on: ubuntu-latest outputs: + artifact_matrix: ${{ steps.set-matrix.outputs.artifact_matrix }} matrix: ${{ steps.set-matrix.outputs.matrix }} env: TARGET: tier1 JOBS: ${{ inputs.jobs_to_run }} steps: &generate_matrix - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Download sjsonnet run: | curl -L -o sjsonnet https://github.com/databricks/sjsonnet/releases/download/0.5.7/sjsonnet-0.5.7-linux-x86_64 @@ -38,13 +47,17 @@ jobs: - name: Extract job matrix id: set-matrix run: | - python3 .github/scripts/extract_matrix.py ./sjsonnet ./ci.jsonnet ${TARGET} ${JOBS} > matrix.json + python3 .github/scripts/extract_matrix.py ./sjsonnet ./ci.jsonnet ${TARGET} ${JOBS} --artifact-mode providers > artifact-matrix.json + python3 .github/scripts/extract_matrix.py ./sjsonnet ./ci.jsonnet ${TARGET} ${JOBS} --artifact-mode non-providers > matrix.json + cat artifact-matrix.json cat matrix.json + echo "artifact_matrix=$(cat artifact-matrix.json)" >> $GITHUB_OUTPUT echo "matrix=$(cat matrix.json)" >> $GITHUB_OUTPUT generate-tier2: runs-on: ubuntu-latest outputs: + artifact_matrix: ${{ steps.set-matrix.outputs.artifact_matrix }} matrix: ${{ steps.set-matrix.outputs.matrix }} env: TARGET: tier2 @@ -54,19 +67,20 @@ jobs: generate-tier3: runs-on: ubuntu-latest outputs: + artifact_matrix: ${{ steps.set-matrix.outputs.artifact_matrix }} matrix: ${{ steps.set-matrix.outputs.matrix }} env: TARGET: tier3 JOBS: ${{ inputs.jobs_to_run }} steps: *generate_matrix - tier1: + tier1-artifacts: needs: generate-tier1 runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - include: ${{ fromJson(needs.generate-tier1.outputs.matrix) }} + include: ${{ fromJson(needs.generate-tier1.outputs.artifact_matrix) }} steps: &buildsteps - name: Process matrix downloads if: ${{ matrix.downloads_steps }} @@ -96,7 +110,7 @@ jobs: "$($pair.Name)=$value" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 } - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: main fetch-depth: ${{ matrix.fetch_depth }} @@ -111,7 +125,7 @@ jobs: "PARENT_DIRECTORY=$PWD" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - uses: actions/setup-python@v6 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 if: ${{ matrix.python_version }} with: python-version: ${{ matrix.python_version }} @@ -164,9 +178,9 @@ jobs: git config --global http.timeout 600 git clone https://github.com/graalvm/mx if [[ "${RUNNER_OS}" == "Windows" ]]; then - ./mx/mx.cmd fetch-jdk -A --jdk-id labsjdk-ce-latest + ./mx/mx.cmd fetch-jdk -A --configuration main/ci/graal/common.json --jdk-id labsjdk-ce-latest else - ./mx/mx fetch-jdk -A --jdk-id labsjdk-ce-latest + ./mx/mx fetch-jdk -A --configuration main/ci/graal/common.json --jdk-id labsjdk-ce-latest fi git -C mx checkout ${{ matrix.mx_version }} @@ -191,12 +205,11 @@ jobs: "$M2" | Out-File -FilePath "$env:GITHUB_PATH" -Append - name: Download artifacts - uses: actions/download-artifact@v5 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 if: ${{ matrix.require_artifact }} with: pattern: ${{ matrix.require_artifact[0] }} merge-multiple: true - continue-on-error: true - name: Export artifact paths Linux if: ${{ matrix.require_artifact }} @@ -211,7 +224,7 @@ jobs: - name: Install MSBuild if: ${{ runner.os == 'Windows' }} - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d # v1.0.2 - name: Setup working-directory: main @@ -258,7 +271,7 @@ jobs: tar cf ${{ matrix.provide_artifact[0] }}.tar ${{ matrix.provide_artifact[1] }} - name: Upload artifacts if: ${{ matrix.provide_artifact }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ matrix.provide_artifact[0] }} path: main/${{ matrix.provide_artifact[0] }}.tar @@ -266,7 +279,7 @@ jobs: - name: Upload logs if: ${{ matrix.logs }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 continue-on-error: true with: name: ${{ format('{0}_logs', matrix.name) }} @@ -274,21 +287,60 @@ jobs: ${{ matrix.logs }} retention-days: ${{ inputs.logs_retention_days || 15 }} if-no-files-found: ignore + + - name: Upload test reports + if: ${{ inputs.export_test_reports && (success() || failure()) }} + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + continue-on-error: true + with: + name: ${{ format('{0}_test_reports', matrix.name) }} + path: /tmp/test-report-*.json + retention-days: 1 - tier2: - if: ${{ success() || inputs.jobs_to_run }} + tier1: + if: ${{ always() && needs.generate-tier1.result == 'success' && needs.tier1-artifacts.result == 'success' }} + needs: [generate-tier1, tier1-artifacts] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: ${{ fromJson(needs.generate-tier1.outputs.matrix) }} + steps: *buildsteps + + tier2-artifacts: + if: ${{ always() && needs.generate-tier2.result == 'success' && (needs.tier1.result == 'success' || inputs.jobs_to_run) }} needs: [generate-tier2, tier1] runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: ${{ fromJson(needs.generate-tier2.outputs.artifact_matrix) }} + steps: *buildsteps + + tier2: + if: ${{ always() && needs.generate-tier2.result == 'success' && needs.tier2-artifacts.result == 'success' }} + needs: [generate-tier2, tier2-artifacts] + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: ${{ fromJson(needs.generate-tier2.outputs.matrix) }} steps: *buildsteps - tier3: - if: ${{ success() || inputs.jobs_to_run }} + tier3-artifacts: + if: ${{ always() && needs.generate-tier3.result == 'success' && (needs.tier2.result == 'success' || inputs.jobs_to_run) }} needs: [generate-tier3, tier2] runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: ${{ fromJson(needs.generate-tier3.outputs.artifact_matrix) }} + steps: *buildsteps + + tier3: + if: ${{ always() && needs.generate-tier3.result == 'success' && needs.tier3-artifacts.result == 'success' }} + needs: [generate-tier3, tier3-artifacts] + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci-post-merge.yml b/.github/workflows/ci-post-merge.yml index 892fc02514..67b5ec87b6 100644 --- a/.github/workflows/ci-post-merge.yml +++ b/.github/workflows/ci-post-merge.yml @@ -2,48 +2,196 @@ name: Post Merge Actions on: pull_request: types: [closed] + workflow_run: + workflows: ["Run CI unittests"] + types: [completed] + +permissions: + actions: read + contents: read + issues: write + pull-requests: read jobs: - check-ci-and-notify: + notify-ci-failure: runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.event.workflow_run.conclusion != 'success' steps: - - uses: actions/github-script@v8 + - name: Find failed CI run + id: failed-ci + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const GRAALVMBOT_LOGIN = "graalvmbot"; - const pr = context.payload.pull_request; - if (!pr || !pr.number || pr.state !== "closed") return; + const TARGET_WORKFLOW_NAME = "Run CI unittests"; - const author = pr.user; - const assignees = pr.assignees || []; - if (!author || author.login !== GRAALVMBOT_LOGIN) return; - if (assignees.length !== 1) return; + async function getPullRequest(prNumber) { + const {data: pr} = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber, + }); + return pr; + } - const sha = pr.head.sha; + async function wasClosedByGraalVmBot(prNumber) { + for await (const response of github.paginate.iterator(github.rest.issues.listEvents, { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + per_page: 100, + })) { + for (const event of response.data) { + if (event.event === "closed" && event.actor && event.actor.login === GRAALVMBOT_LOGIN) { + return true; + } + } + } + return false; + } - const runsResp = await github.rest.actions.listWorkflowRunsForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - head_sha: sha, - event: "pull_request", - per_page: 10 - }); + async function isEligiblePullRequest(pr, checkCloseActor) { + if (!pr || !pr.number || pr.state !== "closed") { + console.log("PR is not closed. Skipping CI failure notification."); + return false; + } + if (checkCloseActor && !(await wasClosedByGraalVmBot(pr.number))) { + console.log(`PR #${pr.number} was not closed by ${GRAALVMBOT_LOGIN}. Skipping CI failure notification.`); + return false; + } + + const assignees = pr.assignees || []; + if (assignees.length !== 1) { + console.log(`Expected exactly 1 assignee, found ${assignees.length}. Skipping CI failure notification.`); + return false; + } + return true; + } + + async function alreadyCommented(prNumber, marker) { + for await (const response of github.paginate.iterator(github.rest.issues.listComments, { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + per_page: 100, + })) { + if (response.data.some(comment => comment.body && comment.body.includes(marker))) { + return true; + } + } + return false; + } + + let pr = null; + let failedRun = null; + let checkCloseActor = false; + + if (context.eventName === "pull_request") { + pr = context.payload.pull_request; + const sender = context.payload.sender; + if (!sender || sender.login !== GRAALVMBOT_LOGIN) { + console.log(`PR closed by ${sender ? sender.login : "unknown"}, not ${GRAALVMBOT_LOGIN}. Skipping CI check.`); + return; + } + if (!(await isEligiblePullRequest(pr, false))) { + return; + } - const failedRun = runsResp.data.workflow_runs.find(run => - run.head_sha === sha && - run.status === "completed" && - run.conclusion !== "success" - ); + const runsResp = await github.rest.actions.listWorkflowRuns({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: "ci-unittests.yml", + head_sha: pr.head.sha, + }); + failedRun = runsResp.data.workflow_runs.find(run => + run.name === TARGET_WORKFLOW_NAME && + run.head_sha === pr.head.sha && + run.status === "completed" && + run.conclusion !== "success" + ); + if (!failedRun) { + console.log("No completed failed CI workflow found for the PR yet. The workflow_run trigger will handle a later failure."); + return; + } + } else if (context.eventName === "workflow_run") { + const run = context.payload.workflow_run; + if (!run || run.name !== TARGET_WORKFLOW_NAME) { + console.log("Not the target workflow_run event."); + return; + } + if (run.conclusion === "success") { + console.log("CI workflow succeeded. No notification needed."); + return; + } + if (!run.pull_requests || run.pull_requests.length === 0) { + console.log("Workflow run has no associated pull request."); + return; + } - if (!failedRun) { - console.log("No failed CI workflow found for the PR."); + pr = await getPullRequest(run.pull_requests[0].number); + checkCloseActor = true; + failedRun = run; + } else { + console.log(`Unsupported event: ${context.eventName}`); return; } + if (!(await isEligiblePullRequest(pr, checkCloseActor))) { + return; + } + + const marker = ``; + if (await alreadyCommented(pr.number, marker)) { + console.log(`Failure notification was already posted for PR #${pr.number} and SHA ${pr.head.sha}.`); + return; + } + + const assignees = pr.assignees || []; + core.setOutput('assignee', assignees[0].login); + core.setOutput('failed_run_url', failedRun.html_url); + core.setOutput('failed_run_id', failedRun.id); + core.setOutput('pr_number', pr.number); + core.setOutput('comment_marker', marker); + console.log(`Found failed CI workflow: ${failedRun.html_url}`); + - name: Download merged test report + if: ${{ steps.failed-ci.outputs.failed_run_url != '' }} + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + name: merged_test_reports + path: report + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ steps.failed-ci.outputs.failed_run_id }} + continue-on-error: true + - name: Post failure comment + if: ${{ steps.failed-ci.outputs.failed_run_url != '' }} + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const assignee = '${{ steps.failed-ci.outputs.assignee }}'; + const runUrl = '${{ steps.failed-ci.outputs.failed_run_url }}'; + const prNumber = Number('${{ steps.failed-ci.outputs.pr_number }}'); + const marker = '${{ steps.failed-ci.outputs.comment_marker }}'; + + let body = `${marker}\n@${assignee} - CI workflow failed: [View workflow](${runUrl})`; + try { + const reportPath = 'report/merged_test_reports.json'; + if (fs.existsSync(reportPath)) { + const data = JSON.parse(fs.readFileSync(reportPath, 'utf8')); + const failed = data.map(t => t.name); + if (failed.length) { + const list = failed.map(n => `- ${n}`).join('\n'); + body = `${marker}\n@${assignee} - CI workflow failed: [View workflow](${runUrl})\nFailed tests:\n\n${list}`; + } + } + } catch (e) { + console.log(`Error parsing test report: ${e}`); + } + await github.rest.issues.createComment({ - issue_number: pr.number, + issue_number: prNumber, owner: context.repo.owner, repo: context.repo.repo, - body: `@${assignees[0].login} - One or more CI jobs failed - [View details](${failedRun.html_url})` + body, }); - console.log("CI failed, assignee notified.") \ No newline at end of file diff --git a/.github/workflows/ci-unittest-retagger.yml b/.github/workflows/ci-unittest-retagger.yml index 908a3561ae..2429942e48 100644 --- a/.github/workflows/ci-unittest-retagger.yml +++ b/.github/workflows/ci-unittest-retagger.yml @@ -9,6 +9,10 @@ on: description: Job selection (Python regex) required: false +permissions: + actions: read + contents: read + jobs: build-standalone-artifacts: @@ -33,6 +37,11 @@ jobs: merge_all_reports: runs-on: ubuntu-latest + permissions: + actions: read + contents: write + issues: write + pull-requests: write if: always() needs: run-retagger env: @@ -40,12 +49,13 @@ jobs: steps: - name: Actions/Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: main + persist-credentials: true - name: Download reports - uses: actions/download-artifact@v5 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: pattern: python-unittest-retagger* merge-multiple: true @@ -54,8 +64,6 @@ jobs: - name: Init Github branch working-directory: main run: | - git remote set-url origin https://$GH_TOKEN@github.com/${{ github.repository }}.git - git remote -v git config --global user.name "Retagger Workflow" git config --global user.email "Retagger_Workflow@oracle.com" git fetch origin @@ -72,7 +80,7 @@ jobs: declare -a os_list=("linux-x86_64" "linux-aarch64" "win32-AMD64") for os in "${os_list[@]}"; do echo "Merging tags for $os" - python3 .github/scripts/merge_retagger_results.py --dir ../retagger-reports --outfile "../retagger-reports/reports-merged-$os.json" --pattern "*$os*" || true + python3 .github/scripts/merge_reports.py --dir ../retagger-reports --outfile "../retagger-reports/reports-merged-$os.json" --pattern "*$os*" || true python3 graalpython/com.oracle.graal.python.test/src/runner.py merge-tags-from-report "../retagger-reports/reports-merged-$os.json" --platform "$os-github" || true git add -A git commit -m "Apply retags for $os" || true diff --git a/.github/workflows/ci-unittests.yml b/.github/workflows/ci-unittests.yml index 107daedde2..91035de4d8 100644 --- a/.github/workflows/ci-unittests.yml +++ b/.github/workflows/ci-unittests.yml @@ -4,7 +4,41 @@ on: types: [opened, ready_for_review, synchronize] workflow_dispatch: +permissions: + actions: read + contents: read + jobs: + abi-check: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Install libabigail + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y abigail-tools + + - name: Get mx and labsjdk + shell: bash + run: | + git config --global http.timeout 600 + git clone https://github.com/graalvm/mx + ./mx/mx fetch-jdk -A --jdk-id labsjdk-ce-latest + + - name: Setup mx and JAVA_HOME + shell: bash + run: | + echo "$(pwd)/mx/" >> "$GITHUB_PATH" + echo "JAVA_HOME=$HOME/.mx/jdks/labsjdk-ce-latest" >> "$GITHUB_ENV" + echo "JVMCI_VERSION_CHECK=ignore" >> "$GITHUB_ENV" + + - name: Run abi-check + shell: bash + run: mx abi-check + build-standalone-artifacts: if: github.event.pull_request.draft == false && success() uses: ./.github/workflows/ci-matrix-gen.yml @@ -18,4 +52,32 @@ jobs: needs: build-standalone-artifacts uses: ./.github/workflows/ci-matrix-gen.yml with: - jobs_to_run: ^(?!python-svm-build|style).*-gate.*$ \ No newline at end of file + jobs_to_run: ^(?!python-svm-build|style).*-gate.*$ + export_test_reports: true + + collect-reports: + if: always() + needs: run-tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Download test reports + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + pattern: '*_test_reports' + merge-multiple: true + path: test-reports + - name: Merge test reports + run: | + python3 .github/scripts/merge_reports.py \ + --outfile merged_test_reports.json \ + --dir test-reports \ + --pattern "**/*.json" \ + --status-filter "failed" + - name: Upload merged test report + if: always() + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: merged_test_reports + path: merged_test_reports.json + if-no-files-found: ignore diff --git a/.github/workflows/downstream-tests.yml b/.github/workflows/downstream-tests.yml index 1553ece7dd..7c437014da 100644 --- a/.github/workflows/downstream-tests.yml +++ b/.github/workflows/downstream-tests.yml @@ -4,6 +4,10 @@ on: - cron: '0 0 * * 1' workflow_dispatch: +permissions: + actions: read + contents: read + jobs: build-standalone-artifacts: @@ -13,81 +17,50 @@ jobs: logs_retention_days: 0 artifacts_retention_days: 0 - downstream-tests: + downstream-pybind11: needs: build-standalone-artifacts - strategy: - fail-fast: false - matrix: - name: - - pybind11 - - virtualenv - - pyo3 - - pydantic-core - - jiter - - cython - os: - - id: ubuntu-latest - platform: linux - arch: amd64 - - id: macos-latest - platform: macos - arch: aarch64 - - runs-on: ${{ matrix.os.id }} - env: - ARTIFACT_PATH: graalpy-native-standalone${{ matrix.os.platform }} - ARTIFACT_PATH_PREFIX: graalpynative - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - steps: - - name: Install CMake (Linux) - if: ${{ matrix.os.platform == 'linux' && matrix.name == 'pybind11' }} - run: | - sudo apt-get update - sudo apt-get install -y cmake - - - name: Install CMake (Darwin) - if: ${{ matrix.os.platform == 'macos' && matrix.name == 'pybind11' }} - run: | - if brew list cmake >/dev/null 2>&1; then - echo "cmake already installed" - else - brew install cmake - fi - - - name: Install Rust toolchain - if: ${{ matrix.name == 'pyo3' || matrix.name == 'pydantic-core' || matrix.name == 'jiter' }} - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - echo "${HOME}/.cargo/bin" >> $GITHUB_PATH + uses: ./.github/workflows/_downstream-test-common.yml + with: + package: pybind11 + needs_cmake: true - - name: Install uv - if: ${{ matrix.name == 'pydantic-core' }} - run: | - curl -LsSf https://astral.sh/uv/install.sh | sh - echo "$HOME/.local/bin" >> $GITHUB_PATH + downstream-virtualenv: + needs: build-standalone-artifacts + uses: ./.github/workflows/_downstream-test-common.yml + with: + package: virtualenv - - name: Checkout main repository - uses: actions/checkout@v4 + downstream-pyo3: + needs: build-standalone-artifacts + uses: ./.github/workflows/_downstream-test-common.yml + with: + package: pyo3 + needs_rust: true - - name: Get GraalPy EA build - if: ${{ matrix.os.platform == 'macos' }} - run: | - tarball="$(curl -sH "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/graalvm/graalvm-ce-dev-builds/releases/latest" | jq -r --arg artifact "graalpy-community-dev-${{ matrix.os.platform }}-${{matrix.os.arch}}.tar.gz" '.assets[] | select(.name == $artifact) | .browser_download_url')" - curl -sfL "$tarball" | tar xz + downstream-pydantic-core: + needs: build-standalone-artifacts + uses: ./.github/workflows/_downstream-test-common.yml + with: + package: pydantic-core + needs_rust: true + needs_uv: true - - name: Get GraalPy build artifact - if: ${{ matrix.os.platform == 'linux' }} - uses: actions/download-artifact@v5 - with: - name: graalpy-native-standalonelinux - path: ${{ env.ARTIFACT_PATH_PREFIX }} + downstream-jiter: + needs: build-standalone-artifacts + uses: ./.github/workflows/_downstream-test-common.yml + with: + package: jiter + needs_rust: true + needs_uv: true - - name: Unpack GraalPy build artifact - if: ${{ matrix.os.platform == 'linux' }} - run: | - ./.github/scripts/unpack-artifact graalpy-native-standalonelinux - mv $ARTIFACT_PATH_PREFIX/main/mxbuild/linux-amd64/GRAALPY_NATIVE_STANDALONE/ graalpy-native-standalone + downstream-cython: + needs: build-standalone-artifacts + uses: ./.github/workflows/_downstream-test-common.yml + with: + package: cython - - name: Run downstream tests for ${{ matrix.name }} - run: python mx.graalpython/downstream_tests.py graalpy-*/bin/python ${{ matrix.name }} + downstream-oracledb: + needs: build-standalone-artifacts + uses: ./.github/workflows/_downstream-test-oracledb.yml + with: + package: oracledb diff --git a/.github/workflows/platforms-tests.yml b/.github/workflows/platforms-tests.yml index 56dffa31cf..fee0c375d2 100644 --- a/.github/workflows/platforms-tests.yml +++ b/.github/workflows/platforms-tests.yml @@ -4,11 +4,14 @@ on: - cron: '0 0 * * 1' workflow_dispatch: +permissions: + contents: read + jobs: centos-ppc64le-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Build maven artifacts run: | @@ -24,7 +27,7 @@ jobs: mv mxbuild/jdk*/mx.graalpython/public-maven-repo m2repo - name: Install OpenJ9 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0 with: distribution: 'semeru' java-version: '17' @@ -36,11 +39,11 @@ jobs: mvn -f graalpython/com.oracle.graal.python.test.integration/pom.xml -Dcom.oracle.graal.python.test.polyglot.version=25.1.0 -Dcom.oracle.graal.python.test.polyglot_repo=file:///$(pwd)/m2repo --batch-mode -U -Dtruffle.UseFallbackRuntime=true -Dpolyglot.engine.allowUnsupportedPlatform=true -Dpolyglot.engine.userResourceCache=/$(pwd)/user_resource_cache -Dpolyglot.python.UnsupportedPlatformEmulates=linux -Dorg.graalvm.python.resources.exclude=native.files test -Dtest=HelloWorldTests,AttributeTests,BuiltinSubclassTest,ComplexTexts,CreateClassTest,AsyncActionThreadingTest,JavaInteropTest rm -rf $(pwd)/user_resource_cache - - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 with: platforms: ppc64le - - uses: docker/setup-docker-action@v4 + - uses: docker/setup-docker-action@e43656e248c0bd0647d3f5c195d116aacf6fcaf4 # v4.7.0 - name: Test on ppc64le using Docker run: | diff --git a/.gitignore b/.gitignore index f1264a948b..585daaafed 100644 --- a/.gitignore +++ b/.gitignore @@ -98,3 +98,13 @@ pom-mx.xml .aider* *.iprof.gz compile_commands.json +/.jdtls* +/.agent-shell* +.codex +opencode.json +pyrightconfig.json +bench-results.json +/.mx/ +/.mxcache/ +/.mxenv +/.mx-bootstrap-cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 117b64894a..ad7267ec2a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,6 +29,14 @@ repos: entry: bash scripts/pre-commit-pylint.sh types: [text] files: '^mx\.graalpython/.*\.py$' + - id: maven-ide-sync + name: Maven IDE facade sync + language: system + pass_filenames: true + require_serial: true + entry: bash -c 'unset GIT_DIR; exec mx python-pominit "$@"' -- + types: [text] + files: '^(mx\.graalpython/suite\.py|mx\.graalpython/mx_pominit\.py)$' - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: @@ -42,4 +50,5 @@ repos: - id: check-toml exclude: '^graalpython/lib-python/.*' - id: check-added-large-files + exclude: '^abi/abi-.*\.xml' - id: check-symlinks diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..15732ee8f5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,88 @@ +# PROJECT KNOWLEDGE BASE + +## OVERVIEW +GraalPy is an alternative implementation of Python. The reference implementation of Python is CPython and GraalPy aims to be as compatible with CPython as possible. +It consists of: Java (Truffle) + C (CPython C-API compatibility) + Python stdlib/overrides, built and tested via the `mx` build tool. + +## STRUCTURE +```text +./ +├── graalpython/ # Core sources + stdlib + tests (multi-language) +│ ├── com.oracle.graal.python/ # Main Java implementation (Truffle AST, runtime, builtins) +│ ├── com.oracle.graal.python.cext/ # C-API (headers + C sources + adapted CPython modules) +│ ├── com.oracle.graal.python.test/ # Python-level + Java-level tests and runners +│ ├── lib-graalpython/ # GraalPy-specific Python modules/patches +│ └── lib-python/ # Vendored/adapted CPython stdlib + CPython tests +├── mx.graalpython/ # `mx` suite + helper commands (build/test/bench/bisect) +├── scripts/ # Dev utilities (launcher, formatting hooks, codegen) +├── .github/workflows/ # GitHub Actions entrypoints +├── ci/ # CI definitions (jsonnet/libsonnet) +├── docs/ # User + contributor docs +├── benchmarks/ # Benchmark harnesses +└── mxbuild/ # GENERATED build artifacts (ignore for code navigation) +``` + +## WHERE TO LOOK +| Task | Location | Notes | +|------|----------|-------| +| Build / run | `docs/contributor/CONTRIBUTING.md`, `mx.graalpython/` | This repo is `mx`-first. The project build command is `mx python-jvm`; do not substitute generic `mx build` for normal build/test workflows. | +| Java runtime & Truffle nodes | `graalpython/com.oracle.graal.python/src/com/oracle/graal/python/{runtime,nodes,builtins}` | Main interpreter implementation. | +| C-API / native extensions | `graalpython/com.oracle.graal.python.cext/{include,src,modules}` | Mirrors CPython naming; many files are adapted from CPython. | +| Python stdlib overrides | `graalpython/lib-graalpython/` | GraalPy-specific modules executed at startup and/or used by builtins. | +| Vendored stdlib + CPython tests | `graalpython/lib-python/3/` | Large; treat as upstream-ish unless you are explicitly changing stdlib/tests. | +| Python-level tests | `graalpython/com.oracle.graal.python.test/src/tests/` | Includes tagged tests + C-API tests. Runner: `.../src/runner.py`. | +| CI pipelines | `.github/workflows/`, `ci.jsonnet`, `ci/` | Workflows typically drive `mx` gates/tags. | +| Launchers / helper scripts | `scripts/python.sh`, `scripts/*` | `python.sh` is the local launcher wrapper. | + +## CONVENTIONS (DEVIATIONS) +- `mx` is the primary build/test entrypoint; suite definition lives in `mx.graalpython/suite.py`. +- `black` is configured but intentionally **disabled** for this repo (root `pyproject.toml`); line length is 120 and version locked to `23` for consistency. +- Formatting/linting is enforced via **pre-commit** with repo-specific hooks (Eclipse formatter, checkstyle, copyright), and pylint only on `mx.graalpython/*.py`. +- Large generated/build outputs exist in-tree (`mxbuild/`, `*.dist/`); do not use them as the source of truth when navigating code. + +## ANTI-PATTERNS (THIS PROJECT) +- **Do not** base edits or reviews on `mxbuild/**` or `*.dist/**` outputs; change sources under `graalpython/**`, `mx.graalpython/**`, etc. +- To build the project locally, use `mx python-jvm`. Do **not** treat generic `mx build` as the repo's normal build command unless a task explicitly requires something narrower. +- Security reports: follow `SECURITY.md` (do **NOT** file public issues for vulnerabilities). +- C-API: heed CPython-style invariants in headers (e.g. `ceval.h`: **NEVER** nest `Py_BEGIN_ALLOW_THREADS` blocks; avoid mixing PyMem/PyObject allocators with `malloc`). +- Interop: foreign `executable` / `instantiable` objects are **never** called with keyword arguments (see `docs/user/Interoperability.md`). + +## UNIQUE STYLES / GOTCHAS +- Builtins: implemented in Java classes annotated with `@CoreFunctions`; individual operations are Nodes annotated with `@Builtin` (see contributing guide). +- Many operations have shared equivalents under `com.oracle.graal.python.lib`; prefer using/adding shared lib nodes instead of duplicating patterns. +- Parser work may require regenerating golden files (see `docs/contributor/CONTRIBUTING.md`). + +## COMMANDS + +* Import dependent suites / download deps + `mx sforceimport` +* Build the project + `mx python-jvm` +* Run after building + `mx python -c 'print(42)'` +* Common local testing + * Run cpyext tests + `mx graalpytest cpyext` + * Rerun a specific failing test + `mx graalpytest TEST-SELECTOR` + * Run JUnit tests + `mx python-gate --tags python-junit` +* Style / formatting + `mx python-style --fix` + `mx python-gate --tags style` +* Building standalones for benchmarking + - use `mx --env native-ee sforceimports && mx --env native-ee checkout-downstream compiler graal-enterprise` to get the right revisions + - use `mx -p ../graal/vm fetch-jdk -jdk-id labsjdk-ce-latest` and set JAVA_HOME as per that command's output + - use `mx --env jvm-ee-libgraal` and `mx --env native-ee` to build the JAVA and NATIVE standalone distributions + +## NOTES +- When searching for implementation, prefer `graalpython/com.oracle.graal.python/src/...` over vendored `lib-python` unless you are intentionally modifying upstream stdlib/tests. +- If you see very large files under `com.oracle.graal.python.cext/modules/_sqlite/` or `expat/`, treat them as upstream imports/adaptations (patch carefully). + +## PULL REQUESTS AND JIRA TICKETS + +We use Jira and Bitbucket, and each PR should reference a Jira ticket with the form [GR-XXXX] where XXXX is the ticket number. +When asked to open pull requests, agents should ask for the Jira ticket number. +When asked to create a ticket, the `gdev-cli jira` tool can be used to create a ticket for the "Python" component. +When asked to create, run gates on, or check on the builds previously run on a pull request, use the `gdev-cli bitbucket` tool. +When asked to add default reviewers to a graalpython PR, that currently means tim.felgentreff@oracle.com, michael.simacek@oracle.com, florian.angerer@oracle.com and stepan.sindelar@oracle.com. diff --git a/CHANGELOG.md b/CHANGELOG.md index 045f3dae31..d72602b9cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ This changelog summarizes major changes between GraalVM versions of the Python language runtime. The main focus is on user-observable behavior of the engine. ## Version 25.1.0 +* The standalone artifacts now include the Python version name before the Graal version. The new artifacts now start with `graalpy---`. +* Standalone JVM artifacts are no longer released as separate distributions. For standalone deployments, use the GraalPy native artifacts. If you require Java interoperability, use our jbang launcher (`jbang graalpy@oracle/graalpython -c "print('hello from GraalPy')"`) or a custom embedding. +* Add `-X jit=0|1|2` presets to tune startup-heavy or throughput-oriented workloads, and make the GraalPy launcher default to the `jit=1` preset. +* Treat foreign buffer objects as Python buffer-compatible binary objects, so APIs like `memoryview`, `bytes`, `bytearray`, `binascii.hexlify`, and `io.BytesIO` work naturally on them when embedding GraalPy in Java. This allows passing binary data between Python and Java's `ByteBuffer` and `ByteSequence` types with minimal (sometimes zero) copies. +* Add support for [Truffle source options](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/source/Source.SourceBuilder.html#option(java.lang.String,java.lang.String)): + * The `python.Optimize` option can be used to specify the optimization level, like the `-O` (level 1) and `-OO` (level 2) commandline options. + * The `python.NewGlobals` option can be used to run a source with a fresh globals dictionary instead of the main module globals, which is useful for embeddings that want isolated top-level execution. * Intern string literals in source files * Allocation reporting via Truffle has been removed. Python object sizes were never reported correctly, so the data was misleading and there was a non-neglible overhead for object allocations even when reporting was inactive. * Better `readline` support via JLine. Autocompletion and history now works in `pdb` @@ -12,6 +19,14 @@ language runtime. The main focus is on user-observable behavior of the engine. * Add `polyglot.gil_locked_during_interop` context manager. By default, the global interpreter lock (GIL) is unlocked when interacting with objects from another language, to give other Python threads a chance to run in parallel. While this avoids potential deadlocks, repeated unlocking and locking of the GIL can decrease performance, so this context manager can be used to keep the lock held around short-running interop. * Add Github workflows that run our gates from the same job definitions as our internal CI. This will make it easier for contributors opening PRs on Github to ensure code contributions pass the same tests that we are running internally. * Added support for specifying generics on foreign classes, and inheriting from such classes. Especially when using Java classes that support generics, this allows expressing the generic types in Python type annotations as well. +* Added a new `java` backend for the `pyexpat` module that uses a Java XML parser instead of the native `expat` library. It can be useful when running without native access or multiple-context scenarios. This backend is the default when embedding and can be switched back to native `expat` by setting `python.PyExpatModuleBackend` option to `native`. Standalone distribution still defaults to native expat backend. +* Add a new context option `python.UnicodeCharacterDatabaseNativeFallback` to control whether the ICU database may fall back to the native unicode character database from CPython for features and characters not supported by ICU. This requires native access to be enabled and is disabled by default for embeddings. +* Add a new context option `python.AllowSignalHandlers` to control whether Python code may install signal handlers. This is disabled by default for Java embedding and enabled in the standalone. +* Add an experimental `python.InitializationEntropySource` option to control the entropy source used for initialization-only randomness such as hash secret generation and `random.Random(None)` seeding. This means embeddings and tests can select deterministic or externally provided initialization entropy without affecting cryptographically relevant APIs like `os.urandom()` or `random.SystemRandom()`. +* Dispatch `sys.audit` events to hooks registered with `sys.addaudithook`, including audit events raised through the `PySys_Audit` C API. +* Foreign temporal objects (dates, times, and timezones) are now given a Python class corresponding to their interop traits, i.e., `date`, `time`, `datetime`, or `tzinfo`. This allows any foreign objects with these traits to be used in place of the native Python types and Python methods available on these types work on the foreign types. +* Make BouncyCastle an optional dependency for embedding use cases. BouncyCastle is only needed for legacy RSA, DSA, and EC privat keys versions 0 and 1. To support these from Python embeddings, BouncyCastle must now be explicitly enabled by adding the `org.graalvm.python:python-bouncycastle-support` Maven artifact. +* The GraalPy Native standalone on Linux now uses a lower-footprint Native Image garbage collection configuration. This reduces resident set size (RSS) for many workloads, but may increase startup time and warmup time, and can slow down some workloads. ## Version 25.0.1 * Allow users to keep going on unsupported JDK/OS/ARCH combinations at their own risk by opting out of early failure using `-Dtruffle.UseFallbackRuntime=true`, `-Dpolyglot.engine.userResourceCache=/set/to/a/writeable/dir`, `-Dpolyglot.engine.allowUnsupportedPlatform=true`, and `-Dpolyglot.python.UnsupportedPlatformEmulates=[linux|macos|windows]` and `-Dorg.graalvm.python.resources.exclude=native.files`. diff --git a/LICENSE.txt b/LICENSE.txt index 420416e076..b4628b25b9 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,10 +1,6 @@ -Product License - GraalVM Community Edition 23.0 Python Language -Component - -This is a release of GraalVM Community Edition 20.0 Python Language Component. This particular copy of the software is released under Universal Permissive License (UPL) v. 1.0. -Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved +Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved =========================================================================== Universal Permissive License v. 1.0. diff --git a/README.md b/README.md index b0a0b66253..f7ebf55ae6 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,21 @@ GraalPy is a Python 3.12 compliant runtime. It has first-class support for embedding in Java and can turn Python applications into fast, standalone binaries. GraalPy is ready for production running pure Python code and has experimental support for many popular native extension modules. +## Contributing + +This project welcomes contributions from the community. Before submitting a pull request, please [review our contribution guide](./CONTRIBUTING.md). + +If you’re considering contributing to this repository, you need to sign the [Oracle Contributor Agreement](https://www.graalvm.org/community/contributors/) before we can merge your changes. +Also, review the [Code of Conduct](https://www.graalvm.org/community/conduct/) for contributors. + +[![Contributing to Graalpy](https://img.youtube.com/vi/qeAUgXQR1h0/0.jpg)](https://www.youtube.com/watch?v=qeAUgXQR1h0) + ## Why GraalPy? **Low-overhead integration with Java and other languages** * Use [Python in Java](docs/user/Interoperability.md) applications on GraalVM JDK, Oracle JDK, or OpenJDK -* Use JVM tools like [Maven](docs/user/README.md), JFR, or [GraalVM Native Image](docs/user/Native-Images-with-Python.md) +* Use JVM tools like [Maven](docs/user/Embedding-Build-Tools.md), JFR, or [GraalVM Native Image](docs/user/Native-Images-with-Python.md) * Manage Python libraries' system access thanks to GraalPy's [Java-based emulation of Python OS APIs](docs/user/Embedding-Permissions.md) **Compatible with the Python ecosystem** @@ -84,65 +93,13 @@ This means that build tools have to be available and installation will take long We provide [Github actions](scripts/wheelbuilder) to help you build binary packages with the correct dependencies. Thanks to our integration with GraalVM Native Image, we can deploy Python applications as [standalone binary](docs/user/Python-Standalone-Applications.md), all dependencies included. -* Linux +**Quick Installation:** - The easiest way to install GraalPy on Linux is to use [Pyenv](https://github.com/pyenv/pyenv) (the Python version manager). - To install version 25.0.2 using Pyenv, run the following commands: - ```bash - pyenv install graalpy-25.0.2 - ``` - ```bash - pyenv shell graalpy-25.0.2 - ``` - > NOTE: There will be a delay between GraalPy release and its availability on Pyenv. Make sure to update Pyenv. - - Alternatively, you can download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases). - - 1. Find the download that matches the pattern _graalpy-XX.Y.Z-linux-amd64.tar.gz_ or _graalpy-XX.Y.Z-linux-aarch64.tar.gz_ (depending on your platform) and download. - 2. Uncompress the file and update your `PATH` environment variable to include the _graalpy-XX.Y.Z-linux-amd64/bin_ (or _graalpy-XX.Y.Z-linux-aarch64/bin_) directory. +- **Linux/macOS**: `pyenv install graalpy-25.0.2 && pyenv shell graalpy-25.0.2` +- **Windows**: `pyenv install graalpy-25.0.2-windows-amd64` +- **Manual**: Download from [GitHub releases](https://github.com/oracle/graalpython/releases) -* macOS - - The easiest way to install GraalPy on macOS is to use [Pyenv](https://github.com/pyenv/pyenv) (the Python version manager). - To install version 25.0.2 using Pyenv, run the following commands: - ```bash - pyenv install graalpy-25.0.2 - ``` - ```bash - pyenv shell graalpy-25.0.2 - ``` - > NOTE: There will be a delay between GraalPy release and its availability on Pyenv. Make sure to update Pyenv. - - Alternatively, you can download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases). - - 1. Find the download that matches the pattern _graalpy-XX.Y.Z-macos-aarch64.tar.gz_ and download. - 2. Remove the quarantine attribute. - ```bash - sudo xattr -r -d com.apple.quarantine /path/to/graalpy - ``` - For example: - ```bash - sudo xattr -r -d com.apple.quarantine ~/.pyenv/versions/graalpy-25.0.2 - ``` - 3. Uncompress the file and update your `PATH` environment variable to include to the _graalpy-XX.Y.Z-macos-aarch64/bin_ directory. - -* Windows - - The Windows support of GraalPy is still experimental, so not all features and packages may be available. - The easiest way to install GraalPy on Windows is to use [Pyenv-win](https://pyenv-win.github.io/pyenv-win/) (the Python version manager for Windows). - To install version 25.0.2 using Pyenv-win, run the following commands: - ```cmd - pyenv install graalpy-25.0.2-windows-amd64 - ``` - ```cmd - pyenv shell graalpy-25.0.2-windows-amd64 - ``` - > NOTE: There will be a delay between GraalPy release and its availability on Pyenv. Make sure to update Pyenv. - - Alternatively, you can download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases). - - 1. Find the download that matches the pattern _graalpy-XX.Y.Z-windows-amd64.tar.gz_ and download. - 2. Uncompress the file and update your `PATH` variable to include to the _graalpy-XX.Y.Z-windows-amd64/bin_ directory. +**See the [complete installation guide](docs/user/Standalone-Getting-Started.md) for detailed instructions.**
@@ -161,36 +118,17 @@ The _setup-python_ action supports GraalPy:
Migrating Jython Scripts to GraalPy -Most existing Jython code that uses Java integration will be based on a stable Jython release—however, these are only available in Python 2.x versions. +Most existing Jython code that uses Java integration will be based on a stable Jython release—however, these are only available in Python 2.x versions. To migrate your code from Python 2 to Python 3, follow [the official guide from the Python community](https://docs.python.org/3/howto/pyporting.html). GraalPy provides a [special mode](docs/user/Python-on-JVM.md) to facilitate migration. -To run Jython scripts, you need to use a GraalPy distribution running on the JVM so you can access Java classes from Python scripts. - -* Linux - - 1. Find and download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases) that matches the pattern _graalpy-jvm-XX.Y.Z-linux-amd64.tar.gz_ or _graalpy-jvm-XX.Y.Z-linux-aarch64.tar.gz_ (depending on your platform) and download. - 2. Uncompress the file and update your `PATH` environment variable to include the _graalpy-jvm-XX.Y.Z-linux-amd64/bin_ (or _graalpy-jvm-XX.Y.Z-linux-aarch64/bin_) directory. - 3. Run your scripts with `graalpy --python.EmulateJython`. -* macOS +**Quick Setup:** - 1. Find and download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases) that matches the pattern _graalpy-jvm-XX.Y.Z-macos-aarch64.tar.gz_ and download. - 2. Remove the quarantine attribute. - ```bash - sudo xattr -r -d com.apple.quarantine /path/to/graalpy - ``` - For example: - ```bash - sudo xattr -r -d com.apple.quarantine ~/.pyenv/versions/graalpy-25.0.2 - ``` - 3. Uncompress the file and update your `PATH` environment variable to include to the _graalpy-jvm-XX.Y.Z-macos-aarch64/bin_ directory. - 4. Run your scripts with `graalpy --python.EmulateJython`. +1. Download a GraalPy JVM distribution: `graalpy-jvm-XX.Y.Z-.tar.gz` +2. Extract and add to PATH +3. Run with: `graalpy --python.EmulateJython` -* Windows - - 1. Find and download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases) that matches the pattern _graalpy-jvm-XX.Y.Z-windows-amd64.tar.gz_. - 2. Uncompress the file and update your `PATH` variable to include to the _graalpy-jvm-XX.Y.Z-windows-amd64/bin_ directory. - 3. Run your scripts with `graalpy --python.EmulateJython`. +**See the [complete migration guide](docs/user/Python-on-JVM.md) for detailed instructions.**
@@ -209,13 +147,6 @@ General documentation about [polyglot programming](https://www.graalvm.org/lates The best way to get in touch with us is to join the `#graalpy` channel on [GraalVM Slack][slack] or [tweet us][twitter]. -## Contributing - -This project welcomes contributions from the community. Before submitting a pull request, please [review our contribution guide](./CONTRIBUTING.md). - -If you're thinking about contributing something to this repository, you will need to sign the [Oracle Contributor Agreement](https://www.graalvm.org/community/contributors/) for us to able to merge your work. -Also take a look at the [code of conduct](https://www.graalvm.org/community/conduct/) for contributors. - ## Security Consult the [security guide](./SECURITY.md) for our responsible security vulnerability disclosure process. diff --git a/THIRD_PARTY_LICENSE.txt b/THIRD_PARTY_LICENSE.txt index 364d64f9b8..e27628c4ee 100644 --- a/THIRD_PARTY_LICENSE.txt +++ b/THIRD_PARTY_LICENSE.txt @@ -39,9 +39,9 @@ SOFTWARE. ================================================================================ -Bouncy Castle Crypto API 1.68 +Bouncy Castle Crypto API 1.84 -Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. +Copyright (c) 2000-2023 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software @@ -343,7 +343,7 @@ The command line interpreter is covered by the Apache Software License. See the org/apache/LICENSE file for details. ================================================================================ -xz 5.2.6 +xz 5.8.3 https://git.tukaani.org/?p=xz.git;a=blob;f=COPYING 1 diff --git a/abi/abi-graalpy250.xml b/abi/abi-graalpy250.xml new file mode 100644 index 0000000000..c10cb6711a --- /dev/null +++ b/abi/abi-graalpy250.xml @@ -0,0 +1,1674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/abi/suppressions-base.ini b/abi/suppressions-base.ini new file mode 100644 index 0000000000..70d2faef3c --- /dev/null +++ b/abi/suppressions-base.ini @@ -0,0 +1 @@ +# Base libabigail suppressions for the GraalPy C API ABI check. diff --git a/abi/suppressions-graalpy250.ini b/abi/suppressions-graalpy250.ini new file mode 100644 index 0000000000..49da1eb33e --- /dev/null +++ b/abi/suppressions-graalpy250.ini @@ -0,0 +1,4 @@ +[suppress_variable] + label = Removed ctypes private exported type objects + symbol_name_regexp = ^(PyCArrayType_Type|PyCData_Type|PyCPointerType_Type|PyCSimpleType|PyCStructType_Type|Simple_Type|UnionType_Type)$ + drop = true diff --git a/bisect-benchmark.ini b/bisect-benchmark.ini index c118098366..fc01ba2188 100644 --- a/bisect-benchmark.ini +++ b/bisect-benchmark.ini @@ -3,7 +3,7 @@ # Usage: # - Create a temporary branch based on the main branch (or the bad commit) # - Fill in this configuration file, preferably using the automated script -# graal-enterprise/graalpython-enterprise/scripts/create-bisect-config +# scripts/create_bisect_config.py # - Commit and push the file # - The push command output should give you a link to create a PR. Open it, but # don't create a PR. Instead, you should execute the job on your commit using diff --git a/ci.jsonnet b/ci.jsonnet index b642ed0813..1eb8fa7fcd 100644 --- a/ci.jsonnet +++ b/ci.jsonnet @@ -5,8 +5,8 @@ (import "ci/python-gate.libsonnet") + (import "ci/python-bench.libsonnet") + { - overlay: "ed8f5da14a487e075631a90cf50e7dbca5d171aa", - specVersion: "6", + overlay: "40bd8048e1a6ba45494605955ffe748ae4db20be", + specVersion: "8", // Until buildbot issues around CI tiers are resolved, we cannot use them // tierConfig: self.tierConfig, @@ -17,10 +17,11 @@ RUBYGEMS_MIRROR: "", JEKYLL_THEME_GIT: "", WEBSITE_GIT: "", - STAGING_DEPLOY_CMD: [], + STAGING_DEPLOY_CMD: [["echo", "1"]], GRAAL_ENTERPRISE_GIT: "", CI_OVERLAYS_GIT: "", BENCHMARK_CONFIG_GIT: "", + NUMPY_REPO_GIT: "", PANDAS_REPO_GIT: "", PIP_EXTRA_INDEX_URL: "", WATCHDOG_GIT: "", @@ -30,6 +31,7 @@ npm_config_registry: "", RODINIA_DATASET_ZIP: "", BUILDBOT_COMMIT_SERVICE: "", + INTERNET_ACCESS_ENV: {}, }, codeowners_builds: [], @@ -75,6 +77,50 @@ local watchdog = self.watchdog, local bench_task(bench=null, benchmarks=BENCHMARKS) = super.bench_task(bench=bench, benchmarks=benchmarks), local bisect_bench_task = self.bisect_bench_task, + local internet_access_env = task_spec({ + environment +: $.overlay_imports.INTERNET_ACCESS_ENV, + }), + local oracledb_free_image = "container-registry.oracle.com/database/free:23.26.0.0", + local oracledb_extra_index_urls = std.join(" ", [ + "https://ol-graal.oraclecorp.com/mt_data/graalpy-25.0-repository/", + "https://artifactory.oci.oraclecorp.com/api/pypi/graalpy-wheels-internal-patches-dev-pypi-local/simple", + $.overlay_imports.PIP_EXTRA_INDEX_URL, + ]), + local oracledb_bench_env = task_spec({ + cacheVenv: false, + capabilities +: ["pinp"], + environment +: { + GRAALPY_ORACLEDB_QUIET_SECONDS: "60", + GRAALPY_ORACLEDB_WAIT_TIMEOUT: "600", + PYO_TEST_ADMIN_PASSWORD: "graalpy", + PYO_TEST_ADMIN_USER: "SYSTEM", + PYO_TEST_CONNECT_STRING: "127.0.0.1:1521/FREEPDB1", + PIP_EXTRA_INDEX_URL: oracledb_extra_index_urls, + PIP_ONLY_BINARY: ":all:", + }, + setup: [ + [ + "podman", "run", + "--detach", + "--replace", + "--name", "graalpy-oracledb", + "-p", "1521:1521", + "-e", "ORACLE_PWD=graalpy", + oracledb_free_image, + ], + ] + super.setup, + teardown +: [ + ["podman", "rm", "--force", "graalpy-oracledb"], + ], + evaluate_late +:: { + z_oracledb_podman: function(builder) { + docker: { + image: "buildslave_ol8_podman_rootless", + mount_modules: true, + }, + }, + }, + }), local native_debug_build_env = task_spec({ environment +: { @@ -97,6 +143,9 @@ local manual_interpreter_bench = manual_interpreter_env + task_spec({ name_suffix +:: ["manual-interpreter"], }), + local with_compiler = task_spec({ + dynamic_imports +:: ["/compiler"], + }), // ----------------------------------------------------------------------------------------------------------------- // @@ -118,10 +167,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:00: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), @@ -135,15 +180,10 @@ "linux:aarch64:jdk-latest" : daily + t("01:00:00"), "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), - "windows:amd64:jdk-latest" : daily + t("01:30:00"), + "python-unittest-multi-context": gpgate + require(GPY_NATIVE_STANDALONE) + platform_spec(no_jobs) + platform_spec({ + "linux:amd64:jdk-latest" : tier3, + "linux:aarch64:jdk-latest" : daily + t("02:00:00"), + "windows:amd64:jdk-latest" : daily + t("02:00:00"), }), "python-unittest-jython": gpgate + platform_spec(no_jobs) + platform_spec({ "linux:amd64:jdk21" : daily + t("00:30:00") + require(GPY_JVM21_STANDALONE), @@ -159,16 +199,7 @@ "python-unittest-arrow-storage": gpgate + require(GPY_JVM_STANDALONE) + platform_spec(no_jobs) + platform_spec({ "linux:amd64:jdk-latest" : tier2, }), - "python-unittest-posix": gpgate + platform_spec(no_jobs) + platform_spec({ - "linux:amd64:jdk-latest" : tier2 + require(GPY_JVM_STANDALONE), - "linux:aarch64:jdk-latest" : tier3 + require(GPY_JVM_STANDALONE), - "darwin:aarch64:jdk-latest" : tier3 + require(GPY_JVM_STANDALONE), - }), "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), @@ -179,13 +210,13 @@ "linux:aarch64:jdk21" : daily + t("01:30:00"), "darwin:aarch64:jdk21" : daily + t("01:30:00"), "windows:amd64:jdk21" : daily + t("01:00:00"), - "linux:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), - "linux:aarch64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), - "darwin:aarch64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), - "windows:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), + "linux:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, + "linux:aarch64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, + "darwin:aarch64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, + "windows:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, }), "python-junit-manual-interpreter": gpgate + platform_spec(no_jobs) + manual_interpreter_gate("python-junit") + platform_spec({ - "linux:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST), + "linux:amd64:jdk-latest" : tier3 + require(GRAAL_JDK_LATEST) + with_compiler, }), "python-junit-maven": gpgate_maven + platform_spec(no_jobs) + platform_spec({ "linux:amd64:jdk21" : daily + t("00:30:00"), @@ -215,6 +246,7 @@ logs+: [ "default.iprof.gz", "default.lcov", + "host-inlining.txt.gz", ], }), }), @@ -249,17 +281,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, @@ -277,9 +309,20 @@ "style-ecj": style_gate + task_spec({ tags:: "style,ecjbuild" }) + platform_spec(no_jobs) + platform_spec({ "linux:amd64:jdk-latest" : tier1, }), - // tests with sandboxed backends for various modules (posix, sha3, ctypes, ...) + // tests with sandboxed backends for various modules (posix, sha3, compression, pyexpat, ...) "python-unittest-sandboxed": gpgate_ee + platform_spec(no_jobs) + platform_spec({ - "linux:amd64:jdk-latest" : tier3, + "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") + provide(GPY_JVM21_STANDALONE), + "linux:amd64:jdk-latest" : tier2 + batches(2) + require(GPY_JVM_STANDALONE), + "linux:aarch64:jdk-latest" : tier3 + require(GPY_JVM_STANDALONE), + "darwin:aarch64:jdk-latest" : tier3 + require(GPY_JVM_STANDALONE), + }), + "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), + "windows:amd64:jdk21" : daily + t("02:00:00") + require(GPY_JVM21_STANDALONE), }), "python-svm-unittest-sandboxed": gpgate_ee + platform_spec(no_jobs) + platform_spec({ "linux:amd64:jdk-latest" : tier3 + provide(GPYEE_NATIVE_STANDALONE), @@ -305,9 +348,7 @@ // not specified as the first arg to `bench_task`. local bench_task_dict = { [bench]: bench_task(bench) + platform_spec(no_jobs) + bench_variants({ - "vm_name:graalvm_ce_default" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:graalvm_ee_default" : {"linux:amd64:jdk-latest" : post_merge + t("08:00:00") + need_pgo}, - "vm_name:graalpython_core" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:graalpython_enterprise" : {"linux:amd64:jdk-latest" : daily + t("08:00:00"), "job_type:checkup" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")} }, @@ -315,18 +356,28 @@ "vm_name:cpython" : {"linux:amd64:jdk-latest" : monthly + t("04:00:00")}, "vm_name:pypy" : {"linux:amd64:jdk-latest" : on_demand + t("04:00:00")}, }), - for bench in ["micro", "meso", "macro"] + for bench in ["micro", "meso"] + } + { + "macro": bench_task("macro") + internet_access_env + platform_spec(no_jobs) + bench_variants({ + "vm_name:graalvm_ee_default" : {"linux:amd64:jdk-latest" : post_merge + t("08:00:00") + need_pgo}, + "vm_name:graalpython_enterprise" : {"linux:amd64:jdk-latest" : daily + t("08:00:00"), + "job_type:checkup" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")} + }, + "vm_name:graalpython_enterprise_multi" : {"linux:amd64:jdk-latest" : weekly + t("08:00:00")}, + "vm_name:cpython" : {"linux:amd64:jdk-latest" : monthly + t("04:00:00")}, + "vm_name:pypy" : {"linux:amd64:jdk-latest" : on_demand + t("04:00:00")}, + }), + } + { + "macro_oracledb": bench_task("macro_oracledb") + platform_spec(no_jobs) + bench_variants({ + "vm_name:graalpython_enterprise" : {"linux:amd64:jdk-latest" : on_demand + t("02:00:00")}, + }) + oracledb_bench_env, } + { [bench]: bench_task(bench) + platform_spec(no_jobs) + bench_variants({ - "vm_name:graalvm_ce_default" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:graalvm_ee_default" : {"linux:amd64:jdk-latest" : post_merge + t("08:00:00") + need_pgo}, - "vm_name:graalpython_core" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, - "vm_name:graalpython_core_panama" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:graalpython_enterprise" : {"linux:amd64:jdk-latest" : daily + t("08:00:00"), "job_type:checkup" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")} }, "vm_name:graalpython_enterprise_multi" : {"linux:amd64:jdk-latest" : weekly + t("08:00:00")}, - "vm_name:graalpython_enterprise_panama" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:cpython" : {"linux:amd64:jdk-latest" : monthly + t("04:00:00")}, "vm_name:pypy" : {"linux:amd64:jdk-latest" : on_demand + t("04:00:00")}, }), @@ -335,40 +386,22 @@ // "small" benchmarks have their argument set such that they run in a resonable // time in the interpreter and they are used for interpreter benchmarking [bench]: bench_task(bench) + platform_spec(no_jobs) + bench_variants({ - "vm_name:graalvm_ce_default_interpreter" : {"linux:amd64:jdk-latest" : on_demand + t("02:00:00")}, "vm_name:graalvm_ee_default_interpreter" : {"linux:amd64:jdk-latest" : daily + t("02:00:00") + need_pgo}, - "vm_name:graalpython_core_interpreter" : {"linux:amd64:jdk-latest" : on_demand + t("02:00:00")}, - "vm_name:graalpython_core_native_interpreter" : {"linux:amd64:jdk-latest" : on_demand + t("02:00:00")}, + "vm_name:graalvm_ee_default_interpreter_uncached" : {"linux:amd64:jdk-latest" : daily + t("02:00:00") + need_pgo}, "vm_name:graalpython_enterprise_interpreter" : {"linux:amd64:jdk-latest" : weekly + t("02:00:00")}, - "vm_name:graalpython_core_interpreter_multi" : {"linux:amd64:jdk-latest" : on_demand + t("02:00:00")}, - "vm_name:graalpython_core_native_interpreter_multi" : {"linux:amd64:jdk-latest" : on_demand + t("02:00:00")}, "vm_name:cpython" : {"linux:amd64:jdk-latest" : weekly + t("02:00:00")}, }), for bench in ["micro_small", "meso_small"] } + { // benchmarks executed via Java embedding driver [bench]: bench_task(bench) + platform_spec(no_jobs) + bench_variants({ - "vm_name:java_embedding_core_interpreter_multi_shared" : {"linux:amd64:jdk-latest" : weekly + t("02:00:00")}, + "vm_name:java_embedding_enterprise_interpreter_multi_shared" : {"linux:amd64:jdk-latest" : weekly + t("02:00:00")}, }), for bench in ["java_embedding_meso"] } + { [bench]: bench_task(bench) + platform_spec(no_jobs) + bench_variants({ - "vm_name:graalpython_core" : {"linux:amd64:jdk-latest" : on_demand + t("05:00:00") + forks_warmup}, "vm_name:graalpython_enterprise" : {"linux:amd64:jdk-latest" : daily + t("05:00:00") + forks_warmup}, - "vm_name:graalvm_ce_default" : {"linux:amd64:jdk-latest" : on_demand + t("05:00:00") + forks_warmup}, "vm_name:graalvm_ee_default" : {"linux:amd64:jdk-latest" : daily + t("05:00:00") + forks_warmup + need_pgo}, - "vm_name:graalpython_core_multi_tier" : {"linux:amd64:jdk-latest" : on_demand + t("05:00:00") + forks_warmup}, - "vm_name:graalpython_enterprise_multi_tier" : {"linux:amd64:jdk-latest" : weekly + t("05:00:00") + forks_warmup}, - "vm_name:graalvm_ce_default_multi_tier" : {"linux:amd64:jdk-latest" : on_demand + t("05:00:00") + forks_warmup}, - "vm_name:graalvm_ee_default_multi_tier" : {"linux:amd64:jdk-latest" : weekly + t("05:00:00") + forks_warmup + need_pgo}, - "vm_name:graalpython_core_3threads" : {"linux:amd64:jdk-latest" : on_demand + t("05:00:00") + forks_warmup}, - "vm_name:graalpython_enterprise_3threads" : {"linux:amd64:jdk-latest" : weekly + t("05:00:00") + forks_warmup}, - "vm_name:graalvm_ce_default_3threads" : {"linux:amd64:jdk-latest" : on_demand + t("05:00:00") + forks_warmup}, - "vm_name:graalvm_ee_default_3threads" : {"linux:amd64:jdk-latest" : weekly + t("05:00:00") + forks_warmup + need_pgo}, - "vm_name:graalpython_core_multi_tier_3threads" : {"linux:amd64:jdk-latest" : on_demand + t("05:00:00") + forks_warmup}, - "vm_name:graalpython_enterprise_multi_tier_3threads" : {"linux:amd64:jdk-latest" : weekly + t("05:00:00") + forks_warmup}, - "vm_name:graalvm_ce_default_multi_tier_3threads" : {"linux:amd64:jdk-latest" : on_demand + t("05:00:00") + forks_warmup}, - "vm_name:graalvm_ee_default_multi_tier_3threads" : {"linux:amd64:jdk-latest" : weekly + t("05:00:00") + forks_warmup + need_pgo}, "vm_name:pypy" : {"linux:amd64:jdk-latest" : on_demand + t("01:00:00")}, }), for bench in ["warmup"] @@ -382,23 +415,19 @@ } + { // interop benchmarks only for graalpython, weekly is enough [bench]: bench_task(bench) + platform_spec(no_jobs) + bench_variants({ - "vm_name:java_jmh_core" : {"linux:amd64:jdk-latest" : daily + t("04:00:00")}, "vm_name:java_jmh_enterprise" : {"linux:amd64:jdk-latest" : daily + t("04:00:00")}, }), for bench in ["jmh"] } + { // benchmarks with many forks for weekly performance reports [bench + "-forks"]: bench_task(bench) + platform_spec(no_jobs) + bench_variants({ - "vm_name:graalvm_ce_default" : {"linux:amd64:jdk-latest" : on_demand + t("10:00:00") + forks_meso}, "vm_name:graalvm_ee_default" : {"linux:amd64:jdk-latest" : weekly + t("10:00:00") + forks_meso + need_pgo}, }), for bench in ["meso"] } + { // benchmarks with community benchmark suites for external numbers [bench]: bench_task(bench, PY_BENCHMARKS) + platform_spec(no_jobs) + raw_results + bench_variants({ - "vm_name:graalpython_core" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:graalpython_enterprise" : {"linux:amd64:jdk-latest" : weekly + t("08:00:00")}, - "vm_name:graalvm_ce_default" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:graalvm_ee_default" : {"linux:amd64:jdk-latest" : weekly + t("08:00:00") + need_pgo}, "vm_name:cpython_launcher" : {"linux:amd64:jdk-latest" : monthly + t("08:00:00")}, "vm_name:pypy_launcher" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, @@ -407,11 +436,7 @@ } + { // benchmarks with community benchmark suites for external numbers [bench]: bench_task(bench, PY_BENCHMARKS) + platform_spec(no_jobs) + raw_results + bench_variants({ - "vm_name:graalpython_core" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, - "vm_name:graalpython_core_panama" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:graalpython_enterprise" : {"linux:amd64:jdk-latest" : weekly + t("08:00:00")}, - "vm_name:graalpython_enterprise_panama" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, - "vm_name:graalvm_ce_default" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, "vm_name:graalvm_ee_default" : {"linux:amd64:jdk-latest" : weekly + t("08:00:00") + need_pgo}, "vm_name:cpython_launcher" : {"linux:amd64:jdk-latest" : monthly + t("08:00:00")}, "vm_name:pypy_launcher" : {"linux:amd64:jdk-latest" : on_demand + t("08:00:00")}, @@ -449,7 +474,7 @@ packages: { ruby: "==3.2.2", libyaml: "==0.2.5", - mx: "7.34.1", + mx: "==7.82.0", python3: "==3.8.10", }, environment: { @@ -463,11 +488,10 @@ ["export", "PATH=$GEM_HOME/bin:$PATH"], ["gem", "install", "--no-document", "--source", $.overlay_imports.RUBYGEMS_MIRROR, "bundler", "-v", "2.5.9"], ["git", "clone", "-b", "main", $.overlay_imports.JEKYLL_THEME_GIT], - ["cd", "graal-languages-jekyll-theme"], - ["gem", "build", "graal-languages-jekyll-theme.gemspec"], - ["mkdir", "-p", "../docs/site/vendor/cache"], - ["cp", "graal-languages-jekyll-theme-*.gem", "../docs/site/vendor/cache"], - ["cd", "../docs/site"], + ["cd", "docs/site"], + ["bundle", "config", "set", "mirror.https://rubygems.org", $.overlay_imports.RUBYGEMS_MIRROR], + ["bundle", "config", "set", "--local", "local.graal-languages-jekyll-theme", "../../graal-languages-jekyll-theme"], + ["bundle", "update", "graal-languages-jekyll-theme"], ["bundle", "install"], ["bundle", "exec", "jekyll", "build"], ], @@ -498,7 +522,7 @@ targets: ["deploy"], capabilities: ["linux", "amd64"], packages: { - mx: "7.34.1", + mx: "==7.82.0", python3: "==3.8.10", }, requireArtifacts: [ diff --git a/ci/AGENTS.md b/ci/AGENTS.md new file mode 100644 index 0000000000..ce604096dc --- /dev/null +++ b/ci/AGENTS.md @@ -0,0 +1,19 @@ +# ci/ — CI DEFINITIONS + +## OVERVIEW +Jsonnet/libsonnet CI definitions consumed by GitHub Actions matrix generation. + +## WHERE TO LOOK +| Task | Location | Notes | +|------|----------|-------| +| Main pipelines | `../ci.jsonnet`, `python-gate.libsonnet` | Defines gates/tags executed in CI. | +| Shared constants | `constants.libsonnet` | Shared settings used across CI definitions. | +| Helpers | `utils.libsonnet` | Common functions/macros. | + +## CONVENTIONS +- GitHub workflows typically call `ci-matrix-gen.yml`, which evaluates `ci.jsonnet` and these libraries. + +## ANTI-PATTERNS +- Don’t encode secrets or environment-specific paths here. +- Keep changes compatible with matrix generation (small diffs, deterministic output). +- NEVER edit CI files in `./graal` diff --git a/ci/constants.libsonnet b/ci/constants.libsonnet index b43b97d84f..53809d09e7 100644 --- a/ci/constants.libsonnet +++ b/ci/constants.libsonnet @@ -35,16 +35,12 @@ NOTIFY_GROUPS:: ["tim.felgentreff@oracle.com"], local ENV = { - graalpy_svm_ce: ["--env", "native-ce"], graalpy_svm_ee: ["--env", "native-ee"], - graalpy_jvm_ce: ["--env", "jvm-ce-libgraal"], graalpy_jvm_ee: ["--env", "jvm-ee-libgraal"], - libgraal_ce: ["--env", "../../graal/vm/mx.vm/libgraal"], libgraal_ee: ["--env", "../../graal-enterprise/vm-enterprise/mx.vm-enterprise/libgraal-enterprise"], }, local DY = { - ce: "/vm", ee: "/vm-enterprise,/graalpython-enterprise", }, @@ -61,16 +57,15 @@ default_multi: "default-multi", interpreter: "interpreter", interpreter_manual: "interpreter-manual", + interpreter_uncached: "interpreter-uncached", native_interpreter: "native-interpreter", native_interpreter_manual: "native-interpreter-manual", interpreter_multi: "interpreter-multi", native_interpreter_multi: "native-interpreter-multi", - default_multi_tier: "default-multi-tier", native: "native", native_manual: "native-manual", native_multi: "native-multi", launcher: "launcher", - panama: "panama", }, local JAVA_EMBEDDING_VM_CONFIG = { @@ -80,33 +75,16 @@ // the host VMs local JVM_VM = { - graaljdk_ce: { - dy: ["--dynamicimports", DY.ce], - env: ENV.graalpy_jvm_ce, - edition: 'ce', - }, graaljdk_ee: { dy: ["--dynamicimports", DY.ee], env: ENV.graalpy_jvm_ee, edition: 'ee', }, - graal_native_image_ce: { - dy: ["--dynamicimports", DY.ce], - env: ENV.graalpy_svm_ce, - edition: 'ce', - }, graal_native_image_ee: { dy: ["--dynamicimports", DY.ee], env: ENV.graalpy_svm_ee, edition: 'ee', }, - server_libgraal_ce: { - jvm: 'server', - jvm_config: 'graal-core-libgraal', - dy: ["--dynamicimports", DY.ce], - env: ENV.libgraal_ce, - edition: 'ce', - }, server_libgraal_ee: { jvm: 'server', jvm_config: 'graal-enterprise-libgraal', @@ -140,6 +118,10 @@ python_vm: PYVM.graalpython, python_vm_config: PYVM_CONFIG.interpreter_manual, }, + graalpython_interpreter_uncached: { + python_vm: PYVM.graalpython, + python_vm_config: PYVM_CONFIG.interpreter_uncached, + }, graalpython_native_interpreter: { python_vm: PYVM.graalpython, python_vm_config: PYVM_CONFIG.native_interpreter, @@ -160,10 +142,6 @@ python_vm: PYVM.graalpython, python_vm_config: PYVM_CONFIG.native_interpreter_multi, }, - graalpython_multi_tier: { - python_vm: PYVM.graalpython, - python_vm_config: PYVM_CONFIG.default_multi_tier, - }, graalpython_native: { python_vm: PYVM.graalpython, python_vm_config: PYVM_CONFIG.native, @@ -176,10 +154,6 @@ python_vm: PYVM.graalpython, python_vm_config: PYVM_CONFIG.native_multi, }, - graalpython_panama: { - python_vm: PYVM.graalpython, - python_vm_config: PYVM_CONFIG.panama, - }, java_embedding_multi_shared: { python_vm: PYVM.graalpython, python_vm_config: JAVA_EMBEDDING_VM_CONFIG.java_embedding_multi_shared, @@ -208,55 +182,25 @@ local VM = { // graalpy jvm standalones - graalpython_core: PYTHON_VM.graalpython + JVM_VM.graaljdk_ce, - graalpython_core_manual: PYTHON_VM.graalpython_manual + JVM_VM.graaljdk_ce, - graalpython_core_interpreter: PYTHON_VM.graalpython_interpreter + JVM_VM.graaljdk_ce, - graalpython_core_interpreter_manual: PYTHON_VM.graalpython_interpreter_manual + JVM_VM.graaljdk_ce, - graalpython_core_multi: PYTHON_VM.graalpython_multi + JVM_VM.graaljdk_ce, - graalpython_core_interpreter_multi: PYTHON_VM.graalpython_interpreter_multi + JVM_VM.graaljdk_ce, - graalpython_core_multi_tier: PYTHON_VM.graalpython_multi_tier + JVM_VM.graaljdk_ce, graalpython_enterprise: PYTHON_VM.graalpython + JVM_VM.graaljdk_ee, graalpython_enterprise_manual: PYTHON_VM.graalpython_manual + JVM_VM.graaljdk_ee, graalpython_enterprise_multi: PYTHON_VM.graalpython_multi + JVM_VM.graaljdk_ee, - graalpython_enterprise_multi_tier: PYTHON_VM.graalpython_multi_tier + JVM_VM.graaljdk_ee, graalpython_enterprise_interpreter: PYTHON_VM.graalpython_interpreter + JVM_VM.graaljdk_ee, graalpython_enterprise_interpreter_manual: PYTHON_VM.graalpython_interpreter_manual + JVM_VM.graaljdk_ee, - graalpython_core_native: PYTHON_VM.graalpython_native + JVM_VM.graaljdk_ce, - graalpython_core_native_manual: PYTHON_VM.graalpython_native_manual + JVM_VM.graaljdk_ce, - graalpython_core_native_interpreter: PYTHON_VM.graalpython_native_interpreter + JVM_VM.graaljdk_ce, - graalpython_core_native_interpreter_manual: PYTHON_VM.graalpython_native_interpreter_manual + JVM_VM.graaljdk_ce, - graalpython_core_native_multi: PYTHON_VM.graalpython_native_multi + JVM_VM.graaljdk_ce, - graalpython_core_native_interpreter_multi: PYTHON_VM.graalpython_native_interpreter_multi + JVM_VM.graaljdk_ce, graalpython_enterprise_native: PYTHON_VM.graalpython_native + JVM_VM.graaljdk_ee, graalpython_enterprise_native_manual: PYTHON_VM.graalpython_native_manual + JVM_VM.graaljdk_ee, graalpython_enterprise_native_multi: PYTHON_VM.graalpython_native_multi + JVM_VM.graaljdk_ee, - graalpython_core_panama: PYTHON_VM.graalpython_panama + JVM_VM.graaljdk_ce, - graalpython_enterprise_panama: PYTHON_VM.graalpython_panama + JVM_VM.graaljdk_ee, // graalpy native standalones - graalvm_ce_default: PYTHON_VM.graalpython + JVM_VM.graal_native_image_ce, - graalvm_ce_default_interpreter: PYTHON_VM.graalpython_interpreter + JVM_VM.graal_native_image_ce, graalvm_ee_default: PYTHON_VM.graalpython + JVM_VM.graal_native_image_ee, graalvm_ee_default_manual: PYTHON_VM.graalpython_manual + JVM_VM.graal_native_image_ee, graalvm_ee_default_interpreter: PYTHON_VM.graalpython_interpreter + JVM_VM.graal_native_image_ee, + graalvm_ee_default_interpreter_uncached: PYTHON_VM.graalpython_interpreter_uncached + JVM_VM.graal_native_image_ee, graalvm_ee_default_interpreter_manual: PYTHON_VM.graalpython_interpreter_manual + JVM_VM.graal_native_image_ee, - graalvm_ce_default_multi_tier: PYTHON_VM.graalpython_multi_tier + JVM_VM.graal_native_image_ce, - graalvm_ee_default_multi_tier: PYTHON_VM.graalpython_multi_tier + JVM_VM.graal_native_image_ee, - - // only 3 compiler threads - graalpython_core_3threads: PYTHON_VM.graalpython + JVM_VM.graaljdk_ce + {python_vm_config: super.python_vm_config + "-3-compiler-threads"}, - graalpython_enterprise_3threads: PYTHON_VM.graalpython + JVM_VM.graaljdk_ee + {python_vm_config: super.python_vm_config + "-3-compiler-threads"}, - graalvm_ce_default_3threads: PYTHON_VM.graalpython + JVM_VM.graal_native_image_ce + {python_vm_config: super.python_vm_config + "-3-compiler-threads"}, - graalvm_ee_default_3threads: PYTHON_VM.graalpython + JVM_VM.graal_native_image_ee + {python_vm_config: super.python_vm_config + "-3-compiler-threads"}, - graalpython_core_multi_tier_3threads: PYTHON_VM.graalpython_multi_tier + JVM_VM.graaljdk_ce + {python_vm_config: super.python_vm_config + "-3-compiler-threads"}, - graalpython_enterprise_multi_tier_3threads: PYTHON_VM.graalpython_multi_tier + JVM_VM.graaljdk_ee + {python_vm_config: super.python_vm_config + "-3-compiler-threads"}, - graalvm_ce_default_multi_tier_3threads: PYTHON_VM.graalpython_multi_tier + JVM_VM.graal_native_image_ce + {python_vm_config: super.python_vm_config + "-3-compiler-threads"}, - graalvm_ee_default_multi_tier_3threads: PYTHON_VM.graalpython_multi_tier + JVM_VM.graal_native_image_ee + {python_vm_config: super.python_vm_config + "-3-compiler-threads"}, // Java embedding - java_embedding_core_multi_shared: PYTHON_VM.java_embedding_multi_shared + JVM_VM.server_libgraal_ce, - java_embedding_core_interpreter_multi_shared: PYTHON_VM.java_embedding_interpreter_multi_shared + JVM_VM.server_libgraal_ce, - java_jmh_core: JVM_VM.server_libgraal_ce, + java_embedding_enterprise_multi_shared: PYTHON_VM.java_embedding_multi_shared + JVM_VM.server_libgraal_ee, + java_embedding_enterprise_interpreter_multi_shared: PYTHON_VM.java_embedding_interpreter_multi_shared + JVM_VM.server_libgraal_ee, java_jmh_enterprise: JVM_VM.server_libgraal_ee, // basline vms diff --git a/ci/graal/ci/ci_common/bench-common.libsonnet b/ci/graal/ci/ci_common/bench-common.libsonnet index 27014ae5d7..346d037d7b 100644 --- a/ci/graal/ci/ci_common/bench-common.libsonnet +++ b/ci/graal/ci/ci_common/bench-common.libsonnet @@ -92,14 +92,6 @@ num_threads:: 32, hyperthreading:: false }, - a12c:: common.linux_aarch64 + self._bench_machine + { - machine_name:: "a12c", - capabilities+: ["tmpfs25g"], - numa_nodes:: [0, 1], - default_numa_node:: 0, - num_threads:: 160, - hyperthreading:: false - }, hr350a:: common.linux_aarch64 + self._bench_machine + { machine_name:: "hr350a", capabilities+: ["tmpfs25g"], diff --git a/ci/graal/ci/common.jsonnet b/ci/graal/ci/common.jsonnet index 44c61a7b25..d12957a22c 100644 --- a/ci/graal/ci/common.jsonnet +++ b/ci/graal/ci/common.jsonnet @@ -166,8 +166,8 @@ local common_json = import "../common.json"; "Graal diagnostic output saved in '(?P[^']+)'", # Keep in sync with jdk.graal.compiler.debug.DebugContext#DUMP_FILE_MESSAGE_REGEXP "Dumping debug output to '(?P[^']+)'", - # Keep in sync with com.oracle.svm.hosted.NativeImageOptions#DEFAULT_ERROR_FILE_NAME - " (?P.+/svm_err_b_\\d+T\\d+\\.\\d+_pid\\d+\\.md)", + # Keep in sync with com.oracle.svm.hosted.ProgressReporter#printErrorMessage + "Please inspect the generated error report at: '(?P[^']+)'", # Keep in sync with jdk.graal.compiler.test.SubprocessUtil#makeArgfile "@(?P.*SubprocessUtil-argfiles.*\\.argfile)", # Keep in sync with com.oracle.truffle.api.test.SubprocessTestUtils#makeArgfile @@ -192,19 +192,6 @@ local common_json = import "../common.json"; packages+: if self.os == "windows" then $.devkits["windows-" + self.jdk_name].packages else {}, }, - eclipse: { - downloads+: { - ECLIPSE: { - name: "eclipse", - version: common_json.eclipse.version, - platformspecific: true, - } - }, - environment+: { - ECLIPSE_EXE: "$ECLIPSE/eclipse", - }, - }, - jdt: { environment+: { JDT: "builtin", @@ -288,10 +275,8 @@ local common_json = import "../common.json"; graalnodejs:: { local this = self, - capabilities+: if self.os == "darwin" then ["!darwin_bigsur", "!darwin_monterey", "!darwin_ventura"] else [], packages+: if self.os == "linux" then { cmake: "==3.22.2", - "00:devtoolset": "==12", } else {}, environment+: if self.os == "windows" then { local devkits_version = std.filterMap( @@ -316,7 +301,7 @@ local common_json = import "../common.json"; wasm:: { downloads+: { - WABT_DIR: {name: 'wabt', version: '1.0.37', platformspecific: true}, + WABT_DIR: {name: 'wabt', version: '1.0.41', platformspecific: true}, }, environment+: { WABT_DIR: '$WABT_DIR/bin', @@ -325,7 +310,7 @@ local common_json = import "../common.json"; wasm_ol8:: { downloads+: { - WABT_DIR: {name: 'wabt', version: '1.0.37-ol8', platformspecific: true}, + WABT_DIR: {name: 'wabt', version: '1.0.41-ol8', platformspecific: true}, }, environment+: { WABT_DIR: '$WABT_DIR/bin', @@ -391,7 +376,6 @@ local common_json = import "../common.json"; "*/*.log", "*/svmbuild/*.log", "*/svmbuild/images/*.log", - "*/*/stripped/*.map", "*/callgrind.*", "*.log", ], @@ -503,8 +487,8 @@ local common_json = import "../common.json"; }, local linux = { os:: "linux", capabilities+: [self.os] }, - # Run darwin jobs on Big Sur or later by excluding all older versions - local darwin = { os:: "darwin", capabilities+: [self.os, "!darwin_sierra", "!darwin_mojave", "!darwin_catalina"] }, + # Run darwin jobs on Sonoma or later by excluding all older versions + local darwin = { os:: "darwin", capabilities+: [self.os, "!darwin_bigsur", "!darwin_ventura", "!darwin_monterey"] }, local windows = { os:: "windows", capabilities+: [self.os] }, local amd64 = { arch:: "amd64", capabilities+: [self.arch] }, @@ -538,11 +522,9 @@ local common_json = import "../common.json"; local common = self.deps.mx + self.deps.common_catch_files + self.deps.common_env, local ol_devtoolset = { - packages+: (if self.arch == "aarch64" then { - "00:devtoolset": "==10", # GCC 10.2.1, make 4.2.1, binutils 2.35, valgrind 3.16.1 - } else { - "00:devtoolset": "==11", # GCC 11.2, make 4.3, binutils 2.36, valgrind 3.17 - }), + packages+: { + "00:devtoolset": "==12", # GCC 12.2.1, make 4.3, binutils 2.36, valgrind 3.19 + }, }, linux_amd64: self.linux_amd64_ol7, diff --git a/ci/graal/common.json b/ci/graal/common.json index 9134364c59..62a81cf1bc 100644 --- a/ci/graal/common.json +++ b/ci/graal/common.json @@ -4,7 +4,7 @@ "Jsonnet files should not include this file directly but use ci/common.jsonnet instead." ], - "mx_version": "7.68.10", + "mx_version": "7.82.2", "COMMENT.jdks": "When adding or removing JDKs keep in sync with JDKs in ci/common.jsonnet", "jdks": { @@ -41,22 +41,24 @@ "labsjdk-ee-21": {"name": "labsjdk", "version": "ee-21.0.2+13-jvmci-23.1-b33", "platformspecific": true }, "labsjdk-ee-21Debug": {"name": "labsjdk", "version": "ee-21.0.2+13-jvmci-23.1-b33-debug", "platformspecific": true }, "labsjdk-ee-21-llvm": {"name": "labsjdk", "version": "ee-21.0.2+13-jvmci-23.1-b33-sulong", "platformspecific": true }, - "graalvm-ee-21": {"name": "graalvm-java21", "version": "23.1.10", "platformspecific": true }, + "graalvm-ee-21": {"name": "graalvm", "version": "21.0.10.0.1-ea.02", "platformspecific": true }, "oraclejdk24": {"name": "jpg-jdk", "version": "24", "build_id": "jdk-24.0.1+9", "platformspecific": true, "extrabundles": ["static-libs"]}, - "oraclejdk25": {"name": "jpg-jdk", "version": "25", "build_id": "jdk-25.0.1+8", "platformspecific": true, "extrabundles": ["static-libs"]}, + "oraclejdk25": {"name": "jpg-jdk", "version": "25", "build_id": "jdk-25.0.2+10", "platformspecific": true, "extrabundles": ["static-libs"]}, "graalvm-ee-25-ea": {"name": "graalvm-jdk", "version": "25.0.0", "ea": "36", "platformspecific": true }, - "oraclejdk-latest": {"name": "jpg-jdk", "version": "25", "build_id": "jdk-25.0.1+8", "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-25.0.1+8-jvmci-25.1-b14", "platformspecific": true }, - "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-25.0.1+8-jvmci-25.1-b14-debug", "platformspecific": true }, - "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-25.0.1+8-jvmci-25.1-b14-sulong", "platformspecific": true }, - "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-25.0.1+8-jvmci-25.1-b14", "platformspecific": true }, - "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-25.0.1+8-jvmci-25.1-b14-debug", "platformspecific": true }, - "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-25.0.1+8-jvmci-25.1-b14-sulong", "platformspecific": true } + "oraclejdk-latest": {"name": "jpg-jdk", "version": "25", "build_id": "jdk-25.0.3+9", "platformspecific": true, "extrabundles": ["static-libs"]}, + "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-25.0.3+9-jvmci-25.1-b18", "platformspecific": true }, + "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-25.0.3+9-jvmci-25.1-b18-debug", "platformspecific": true }, + "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-25.0.3+9-jvmci-25.1-b18-sulong", "platformspecific": true }, + "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-25.0.3+9-jvmci-25.1-b18", "platformspecific": true }, + "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-25.0.3+9-jvmci-25.1-b18-debug", "platformspecific": true }, + "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-25.0.3+9-jvmci-25.1-b18-sulong", "platformspecific": true } }, + "default_jdks" : ["labsjdk-ee-latest", "labsjdk-ce-latest"], + "eclipse": { "version": "4.26.0", "short_version": "4.26", @@ -65,8 +67,8 @@ "pip": { "ninja_syntax": "==1.7.2", - "lazy-object-proxy": "==1.6.0", - "pylint": "==2.4.4", + "lazy-object-proxy": "==1.10.0", + "pylint": "==3.2.7", "black": "==23.11.0" } } diff --git a/ci/python-bench.libsonnet b/ci/python-bench.libsonnet index 81aba0fca1..a8f2857110 100644 --- a/ci/python-bench.libsonnet +++ b/ci/python-bench.libsonnet @@ -5,6 +5,7 @@ local run_spec = import "graal/ci/ci_common/run-spec.libsonnet", local tools = import "graal/ci/ci_common/run-spec-tools.libsonnet", local const = import "constants.libsonnet", + local utils = import "utils.libsonnet", local reports = $.overlay_imports.reports, local exclude = run_spec.exclude, @@ -27,7 +28,8 @@ micro: "micro-graalpython:*", micro_native: "micro-native-graalpython:*", meso: "meso-graalpython:*", - macro: "macro-graalpython:*", + macro: "macro-graalpython:~c-oracledb-load", + macro_oracledb: "macro-graalpython:c-oracledb-load", interop: "interop-graalpython:*", warmup: "python-warmup-graalpython:*", micro_small: "micro-small-graalpython:*", @@ -90,11 +92,15 @@ local environment(os, arch) = self.environment(os, arch) + { BENCH_RESULTS_FILE_PATH: "bench-results.json", PANDAS_REPO_URL: $.overlay_imports.PANDAS_REPO_GIT, + NUMPY_REPO_URL: $.overlay_imports.NUMPY_REPO_GIT, + } + if $.overlay_imports.PIP_EXTRA_INDEX_URL != "" then { PIP_EXTRA_INDEX_URL: $.overlay_imports.PIP_EXTRA_INDEX_URL, - }, + } else {}, + + local pip_index_setup = utils.pip_index_setup($.overlay_imports.PIP_EXTRA_INDEX_URL), local packages(os, arch) = self.packages(os, arch) + { - make: ">=3.83", + make: "==4.3", binutils: "==2.23.2", }, @@ -193,7 +199,7 @@ "--pythonjavadriver-vm-config" else super.vm_config_name, - setup+: [ + setup+: pip_index_setup + [ // NOTE: logic shared with ci/python-gate.libsonnet, keep in sync // ensure we get graal-enterprise as a hostvm ["git", "clone", $.overlay_imports.GRAAL_ENTERPRISE_GIT, "${BUILD_DIR}/graal-enterprise"], @@ -240,10 +246,12 @@ downloads: downloads(self.os, self.arch), name: "bisect-benchmark", targets: ['bench'], - logs +: logs(self.os, self.arch), + logs +: logs(self.os, self.arch) + [ + "bisect-benchmark-result.json", + ], deploysArtifacts: true, packages +: packages(self.os, self.arch) + { - "apache/ant": ">=1.9.4", + "apache/ant": "==1.10.1", libyaml: "==0.2.5", "pip:ninja_syntax": "==1.7.2", "pip:pylint": "==2.4.4", diff --git a/ci/python-gate.libsonnet b/ci/python-gate.libsonnet index b6ac88522d..5be4dcfa44 100644 --- a/ci/python-gate.libsonnet +++ b/ci/python-gate.libsonnet @@ -42,9 +42,7 @@ amd64 +: common.darwin_amd64 + { capabilities +: ["ram32gb"], }, - aarch64 +: common.darwin_aarch64 + { - capabilities +: ["darwin_bigsur"], - }, + aarch64 +: common.darwin_aarch64, }, windows +: { amd64 +: common.windows_amd64 + { @@ -83,7 +81,6 @@ linux: { common: { LIBGMP: {name:"libgmp", version:"6.1.0", platformspecific:true}, - NUMPY_BENCHMARKS_DIR: {name: "numpy", version: "1.26.4", platformspecific: false}, PYPY_HOME: {name: "pypy3", version: "3.10-v7.3.12", platformspecific: true}, PYPY_BENCHMARKS_DIR: {name: "pypybenchmarks", version: "84f401a8f55a", platformspecific: false}, }, @@ -142,6 +139,7 @@ BISECT_EMAIL_TO_PATTERN: ".*@oracle.com", TRUFFLE_STRICT_OPTION_DEPRECATION: "true", npm_config_registry: $.overlay_imports.npm_config_registry, + CFLAGS: "-ggdb", }, linux: { common: ENV_POSIX + {}, @@ -195,6 +193,8 @@ buildslave_ol8: ENVIRONMENT_DIFF_OL8, }, + local pip_index_setup = utils.pip_index_setup($.overlay_imports.PIP_EXTRA_INDEX_URL), + //------------------------------------------------------------------------------------------------------------------ // packages //------------------------------------------------------------------------------------------------------------------ @@ -203,30 +203,32 @@ linux: { common: { "00:devtoolset": "==7", - "01:binutils": ">=2.34", - bzip2: ">=1.0.6", - cmake: ">=3.22.2", - zlib: ">=1.2.11", - lcov: ">=1.11", - libffi: ">=3.2.1", + "01:binutils": "==2.34", + bzip2: "==1.0.6", + cmake: "==3.22.2", + zlib: "==1.2.11", + lcov: "==1.14", + libffi: "==3.2.1", llvm: "==8.0.0", - maven: ">=3.3.9", + maven: "==3.9.10", curl: '==7.50.1', }, amd64: {}, - aarch64: {}, + aarch64: { + maven: "==3.5.3", + }, }, darwin: { common: { coreutils: "", - maven: ">=3.3.9", + maven: "==3.3.9", }, amd64: {}, aarch64: {}, }, windows: { common: { - maven: ">=3.3.9", + maven: "==3.3.9", }, amd64: {}, aarch64: {}, @@ -239,8 +241,12 @@ local LOGS = [ "dumps/*/*", "graal_dumps/*/*", + "graal_dumps/*/*/*", + "graal_dumps/*/*/*/*", "bench-results.json", "raw-results.json", + "mxbuild/*/libpythonvm/libpythonvm.so.debug", + "mxbuild/*/GRAALPY_STANDALONE_COMMON/lib/graalpy*/libpython-native.so", ], //------------------------------------------------------------------------------------------------------------------ @@ -277,8 +283,8 @@ local jdk_version = self.jdk_version, local artifact_name = name + os + arch, local capabilities = if (self.os == "darwin" && self.arch != "aarch64") then - // for darwin, amd64: set minimum requirement to bigsur - [c for c in super.capabilities if !std.startsWith(c, "darwin")] + ["darwin_bigsur"] + // for darwin, amd64: set minimum requirements + [c for c in super.capabilities if !std.startsWith(c, "darwin")] else super.capabilities, capabilities: capabilities, @@ -373,17 +379,6 @@ packages(os, arch):: get(PACKAGES, os, arch), - local eclipse = task_spec(evaluate_late({ - // late evaluation of the eclipse mixin, conditional import based on platform - // eclipse downloads are not provided for aarch64 - "eclipse": function(builder) - local arch = builder.arch; - if arch == "aarch64" then - {} - else - common.deps.eclipse - })), - logs(os, arch):: LOGS, //------------------------------------------------------------------------------------------------------------------ @@ -418,14 +413,17 @@ python_version: "3.8", targets: [self.target], logs+: $.logs(self.os, self.arch), + catch_files+: [ + // generated by patch in CPython test support + "HEAP DUMP: (?P.+\\.hprof)", + ], // all gates share the same base set of downloads downloads+: $.downloads(self.os, self.arch), // all gates share the same base environment environment+: $.environment(self.os, self.arch), packages+: $.packages(self.os, self.arch), run+: [ - // TODO: remove inlineVerifierInstrument=false once VerifierInstrument#checkFrameIsEmpty is fixed, GR-72201 - ["mx", "--J", "@-Dtck.inlineVerifierInstrument=false"] + self.mx_parameters + self.dy + self.primary_suite + [ + ["mx"] + self.mx_parameters + self.dy + self.primary_suite + [ "--strict-compliance", "--primary", "gate", "--tags", self.tags, "-B=--force-deprecation-as-warning", ] + self.all_suites + self.gate_parameters, ], @@ -468,7 +466,7 @@ guard+: { excludes+: ["**.md", "docs/**", "3rd_party_licenses.txt", "scripts/**"], }, - setup+: [ + setup+: pip_index_setup + [ // force imports the main repository to get the right graal commit ["mx"] + self.mx_parameters + ["sforceimports"], // logging @@ -499,7 +497,7 @@ dynamic_imports+:: ["/graalpython"], }), - graalpy_eclipse_gate:: $.graalpy_gate + eclipse + jdt, + graalpy_eclipse_gate:: $.graalpy_gate + jdt, unittest_retagger_gate:: $.graalpy_gate + task_spec({ environment+: { diff --git a/ci/utils.libsonnet b/ci/utils.libsonnet index b5a6ea29a2..e89be0f0c6 100644 --- a/ci/utils.libsonnet +++ b/ci/utils.libsonnet @@ -24,6 +24,24 @@ local common = import "graal/ci/common.jsonnet"; else newPath, + pip_index_setup(extra_index_url):: + local pip_index_from_config_or_env = [ + "python", + "-c", + "import os, subprocess, sys; " + + "cmd = [sys.executable, '-m', 'pip', 'config', 'get', 'global.index-url']; " + + "p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True); " + + "print(p.stdout.strip() if p.returncode == 0 else os.environ.get('PIP_INDEX_URL') or " + + "os.environ.get('PIP_EXTRA_INDEX_URL', ''))", + ]; + (if extra_index_url != "" then [ + ["set-export", "PIP_EXTRA_INDEX_URL", extra_index_url], + ] else []) + [ + // Use the CI Python's configured base index if present. pip config get does not report env + // vars, so preserve Buildbot's PIP_INDEX_URL before falling back to the overlay repository URL. + ["set-export", "PIP_INDEX_URL", pip_index_from_config_or_env], + ], + ensure_notify(builds):: [ b + ( if !std.objectHas(b, "notify_groups") && std.objectHas(b, "targets") && (b.targets == ["gate"] || b.targets == ["post-merge"]) then diff --git a/docs/AGENTS.md b/docs/AGENTS.md new file mode 100644 index 0000000000..ef0201d8ee --- /dev/null +++ b/docs/AGENTS.md @@ -0,0 +1,19 @@ +# docs/ — DOCUMENTATION + +## OVERVIEW +Contributor and user documentation; many repo-wide rules live here. + +## WHERE TO LOOK +| Task | Location | Notes | +|------|----------|-------| +| Build/test instructions | `contributor/CONTRIBUTING.md` | Primary “how to build/run gates” guide. | +| Runtime internals | `contributor/IMPLEMENTATION_DETAILS.md` | Interpreter design details. | +| Interop rules | `user/Interoperability.md` | Keyword-arg restrictions for foreign calls, etc. | +| Native extensions | `user/Native-Extensions.md` | Guidance on native packages and tooling. | +| Standalone apps | `user/Python-Standalone-Applications.md` | Notes on packaging and limitations. | + +## CONVENTIONS +- Prefer updating docs here rather than duplicating instructions in random READMEs. + +## ANTI-PATTERNS +- Security: follow `SECURITY.md` at repo root; do not instruct users to file public vulnerability issues. diff --git a/docs/contributor/CONTRIBUTING.md b/docs/contributor/CONTRIBUTING.md index 9b509d92ad..d98a03a95b 100644 --- a/docs/contributor/CONTRIBUTING.md +++ b/docs/contributor/CONTRIBUTING.md @@ -11,9 +11,9 @@ Please also take some time to review our [code of conduct](http://www.graalvm.or ### Using a Github codespace -The devcontainer we create sets up a code workspace on launch—the first time you launch the container, you will need a bit of patience. -Then, use the command palette or the File menu and select "Open Workspace from File" and select [/workspace/graalpython.code-workspace](../../../graalpython.code-workspace). -The VSCode window will reload and open GraalPy and all the related Java projects. +The devcontainer will use checked-in Maven POM facades, so basic Java editing works directly. +Use `mx python-jvm` to build, run, and test a GraalPy distribution artifact. +For full editing support, run `mx vscodeinit` and open the `graalpython.code-workspace` file that command generates. ### Setting up on your machine @@ -63,6 +63,10 @@ This will generate configurations for Eclipse, IntelliJ, and NetBeans so that yo See also the documentation in mx for [setting up your IDE](https://github.com/graalvm/mx/blob/master/docs/IDE.md). If you use another editor (such as VSCode, Emacs, or Neovim) with support for the [Eclipse language server](https://github.com/eclipse/eclipse.jdt.ls) or [Apache NetBeans language server](https://marketplace.visualstudio.com/items?itemName=ASF.apache-netbeans-java), you can also get useable development setups with that, but it's not something we explicitly support. +The checked-in Maven POMs are lightweight facades for quick Java IDE import in editors such as Eclipse, VSCode, or any editor using JDTLS. +They are not the full GraalPy build; use `mx ideinit` for the full generated IDE setup and `mx python-jvm` for the build needed to run or test GraalPy. +The facade uses the `graalvm.version` Maven property for GraalVM artifact resolution; override it locally if you need a different version. + ## Development Layout Besides the source code of the Python interpreter, we have some useful `mx` functions defined under the _mx.graalpython_ directory. @@ -287,14 +291,16 @@ mx benchmark meso:nbody3 \ -Dgraal.MethodFilter=*measure* ``` +For debugging native problems in benchmark runs, there's `BENCHMARK_DEBUG_ARGS` in `mx_graalpython_benchmark.py` to log more stuff for debugging, at the cost of performance. +This is intended for focused reproducer runs on a branch. + ### A note on terminology Note that there may be a little confusion about the configuration names of benchmarks. -#### GraalVM Community Edition and Oracle GraalVM configurations +#### Oracle GraalVM configurations -We have benchmarks for GraalVM Community Edition and Oracle GraalVM. -For historical reasons, these are sometimes referred to in some config files as *CE* and *EE*; *core* and *enterprise*; *graalvm_ce* and *graalvm_ee*; or *graalpython_core* and *graalpython_enterprise*, respectively. +Benchmark CI jobs use Oracle GraalVM configurations. For historical reasons, these are sometimes referred to in config files as *EE*, *enterprise*, *graalvm_ee*, or *graalpython_enterprise*. ### Different GraalVM Python configurations diff --git a/docs/contributor/GETTING_STARTED.md b/docs/contributor/GETTING_STARTED.md new file mode 100644 index 0000000000..1d5d12cd8b --- /dev/null +++ b/docs/contributor/GETTING_STARTED.md @@ -0,0 +1,99 @@ +# Contributing to GraalPy: getting started + +Thanks for considering contributing to GraalPy. +This page aims at helping you through your first contribution to the project. + +For deep technical details and complete command references, see [CONTRIBUTING.md](./CONTRIBUTING.md). + +If you want help while getting started, join the [GraalVM community Slack](https://www.graalvm.org/slack-invitation/). + +## Quick path for your first contribution + +1. [Pick an issue](#1-pick-an-issue) +2. [Set up your environment](#2-set-up-your-environment) +3. [Make a change](#3-make-a-change) +4. [Run focused checks](#4-run-focused-checks) +5. [Open your Pull Request](#5-open-your-pull-request) + +--- + +## 1. Pick an issue + +Start with something small and well-scoped, for instance, issues labeled [good first issue](https://github.com/oracle/graalpython/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22). + +If there is no issue yet, create one from the [issue templates](https://github.com/oracle/graalpython/issues/new/choose) so the work is tracked and discussed. + +![Issue Form](/docs/contributor/assets/issue_form_selector.png) + +If you think you've found a security vulnerability, do not raise a GitHub issue and follow the instructions in our [security policy](https://github.com/oracle/graalpython/blob/master/SECURITY.md). + +## 2. Set up your environment + +You can contribute using one of the following environments: + +- **GitHub Codespaces** (quickest onboarding): see [Using a GitHub codespace](./CONTRIBUTING.md#using-a-github-codespace) +- **Your local machine**: see [Setting up on your machine](./CONTRIBUTING.md#setting-up-on-your-machine) + +Then do the minimal Git setup: + +```bash +git checkout master +git pull +git checkout -b +``` + +Optionally, after setup: + +```bash +mx ideinit +``` + +## 3. Make a change + +Keep your first change small and easy to review (one issue, one objective, one PR). +Before editing, quickly identify where the code or tests live by checking the project structure in the +[Development Layout](https://github.com/oracle/graalpython/blob/master/docs/contributor/CONTRIBUTING.md#development-layout). + +If you use GitHub Codespaces with GitHub Copilot Pro, you can also run an AI coding agent in an isolated environment with access to Issues, PRs, and CI context. + +## 4. Run focused checks + +Before opening a PR, run tests affected by your changes. + +Common commands: + +- `mx clean`: clean build files +- `mx build`: build GraalPy +- `mx python-svm`: build GraalPy native +- `mx gate --tags python-unittest`: Python unit tests +- `mx gate --tags python-junit`: Java/JUnit tests +- `mx graalpytest ::`: run one targeted Python test + +Example: + +```bash +mx graalpytest graalpython/lib-python/3/test/test_threading.py::test.test_threading.ExceptHookTests.test_excepthook +``` + +If you need a complete list of commands, run `mx help` or check [CONTRIBUTING.md](./CONTRIBUTING.md). + +## 5. Open your pull request + +1. Push your branch to your fork +2. Open a PR against `master` +3. Mark it **Ready for review** when it is ready + +Important: + +- You must sign the [Oracle Contributor Agreement (OCA)](https://www.graalvm.org/community/contributors/) before merge +- CI unit tests run when the PR is ready for review (not in draft) +- Opening a PR early in your fork is fine if you want CI feedback + +--- + +## What to read next + +After your first contribution, continue with: + +- [CONTRIBUTING.md](./CONTRIBUTING.md) for full setup, workflows, and CI details +- [IMPLEMENTATION_DETAILS.md](./IMPLEMENTATION_DETAILS.md) for deeper architecture context diff --git a/docs/contributor/IMPLEMENTATION_DETAILS.md b/docs/contributor/IMPLEMENTATION_DETAILS.md index 216a31ee19..7b9cef7609 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,19 +264,19 @@ 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`. -* When NFI calls `toNative`/`asPointer`, we: +* When a C API transition needs a native pointer for a managed object, we: * Allocate a native stub that will represent the object on the native side. We allocate room for the `refcount` and type pointer to avoid upcalls for reading those. For some types such as floats, we also store the diff --git a/docs/contributor/assets/issue_form_selector.png b/docs/contributor/assets/issue_form_selector.png new file mode 100644 index 0000000000..d0c8cc64c5 Binary files /dev/null and b/docs/contributor/assets/issue_form_selector.png differ diff --git a/docs/site/01-docs.md b/docs/site/01-docs.md deleted file mode 100644 index 9a76c5d718..0000000000 --- a/docs/site/01-docs.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -layout: docs -title: Documentation -permalink: docs/ ---- - -{% gfm_docs ../user/README.md %} -{% gfm_docs ../user/Python-Runtime.md %} -{% gfm_docs ../user/Performance.md %} -{% gfm_docs ../user/Python-on-JVM.md %} - -

-Python Context Options -

-Below are the options you can set on contexts for GraalPy. -{% python_options ../../graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java %} - -{% gfm_docs ../user/Native-Images-with-Python.md %} -{% gfm_docs ../user/Python-Standalone-Applications.md %} -{% gfm_docs ../user/Interoperability.md %} -{% gfm_docs ../user/Embedding-Build-Tools.md %} -{% gfm_docs ../user/Embedding-Permissions.md %} -{% gfm_docs ../user/Tooling.md %} -{% gfm_docs ../user/Troubleshooting.md %} -{% gfm_docs ../user/Test-Tiers.md %} - -{% copy_assets ../user/assets %} diff --git a/docs/site/01-jvm-developers.md b/docs/site/01-jvm-developers.md new file mode 100644 index 0000000000..8b5be75382 --- /dev/null +++ b/docs/site/01-jvm-developers.md @@ -0,0 +1,501 @@ +--- +layout: base +permalink: jvm-developers/ +audience_identifier: jvm +--- + +
+
+
+
+

Embed Python in JVM Applications with GraalPy

+
+
+
+ access icon +
+
+

Python Packages in Java

+
+
+
Use Python packages directly in Java, Kotlin, or Scala
+
+
+
+
+ speed icon +
+
+

Jython Upgrade Path

+
+
+
Upgrade Jython projects to Python 3
+
+
+
+
+ upgrade icon +
+
+

JVM Scripting with Python

+
+
+
Script JVM applications with Python
+
+
+
+
+
+
+
+ +
+
+
+
+

How to Get Started

+
+
Add GraalPy as a dependency to your JVM application, or go straight to the starter project.
+
+
+
+
+

Without Python Dependencies

+
+
+
+ + +
+
+ +
+
+
+{%- highlight groovy -%} +dependencies { + implementation("org.graalvm.polyglot:polyglot:{{ site.language_version }}") + implementation("org.graalvm.polyglot:python:{{ site.language_version }}") +} +{%- endhighlight -%} + +
+
+
+
+{%- highlight java -%} +import org.graalvm.polyglot.Context; + +try (Context context = Context.newBuilder().build()) { + context.eval("python", "print('Hello from GraalPy!')"); +} +{%- endhighlight -%} + +
+
+
+
+
+
+

With Python Dependencies

+
+
+
+ + +
+
+ +
+
+
+{%- highlight groovy -%} +plugins { + id "org.graalvm.python" version "{{ site.language_version}}" +} + +dependencies { + implementation("org.graalvm.python:python-embedding:{{ site.language_version }}") +} + +graalPy { + packages = ["pyfiglet==1.0.2"] +} +{%- endhighlight -%} + +
+
+
+
+{%- highlight java -%} +import org.graalvm.polyglot.Context; +import org.graalvm.python.embedding.GraalPyResources; + +try (Context context = GraalPyResources.contextBuilder().build()) { + context.eval("python", """ + from pyfiglet import Figlet + f = Figlet(font='slant') + print(f.renderText('Hello from GraalPy!')) + """); +} +{%- endhighlight -%} + +
+
+
+
+
+
+
+
+
+ + + + +
+ +
+ +
+ +
+ +
+
+
+
+

Videos

+
+ +
+
+

Tips and Tricks for GraalVM and Graal Languages

+
+
+
In this session, Fabio Niephaus from the GraalVM team shows his favourite tips and tricks for using GraalPy and other Graal Languages in IntelliJ IDEA. He also shows how to use IntelliJ IDEA as a multi-language IDE. Language injections and support for various debugging protocols make it easy to embed and debug code written in languages like Python in Java applications. +
+
+
+
+
+ +
+
+

Supercharge your Java Applications with Python!
Jfokus'25

+
+
+
Projects such as LangChain4j, Spring AI, and llama3.java got the Java community very excited about AI in the last year. + The Python ecosystem also provides many powerful packages for data science, machine learning, and more. + Wouldn't it be cool if you, as a Java developer, could benefit from this, too? +
+ In this talk, we show how you can get started with GraalPy and use packages from the Python ecosystem. + We also show some live demos and preview upcoming features that will improve the interaction between Java and native extensions that ship with popular Python packages. +
+
+
+
+
+
+
+
diff --git a/docs/site/01-python-developers.md b/docs/site/01-python-developers.md new file mode 100644 index 0000000000..4e0afdc721 --- /dev/null +++ b/docs/site/01-python-developers.md @@ -0,0 +1,244 @@ +--- +layout: base +permalink: python-developers/ +audience_identifier: python +--- + +
+
+
+
+

Build and Run Python Applications with GraalPy

+
+
+
+ compatibility icon +
+
+

High-Performance Python

+
+
+
Speed up Python applications with the Graal JIT compiler
+
+
+
+
+ binary icon +
+
+

Single-Binary Packaging

+
+
+
Package Python applications as a single binary
+
+
+
+
+ code icon +
+
+

Java Interoperability

+
+
+
Use Java libraries in Python applications
+
+
+
+
+
+
+
+ +
+ +
+
+
+

Install or Download

+
+
+ GraalPy is available for multiple platforms in two variants: Native (for a compact download size and smaller footprint) and JVM (for full Java interoperability). Distributions based on Oracle GraalVM provide the best performance and advanced features and are released under the GFTC license. Distributions based on GraalVM Community Edition, released under the OSI-approved UPL license, are available on GitHub. + See Choosing a GraalPy Distribution for guidance on selecting the appropriate runtime. +
+
+
+
+
+{%- highlight bash -%} +# Latest GraalPy release +pyenv install graalpy-{{ site.language_version }} +pyenv shell graalpy-{{ site.language_version }} + +# On Windows (pyenv-win), provide platform-specific names +pyenv install graalpy-{{ site.language_version }}-windows-amd64 +pyenv shell graalpy-{{ site.language_version }}-windows-amd64 + +# Latest development build of GraalPy +pyenv install graalpy-dev +pyenv shell graalpy-dev +{%- endhighlight -%} +
+
+
+ + pyenv badge + +
+
+
+
+
+{%- highlight bash -%} +# Install GraalPy with uv (uv selects GraalPy by Python language version) +uv python install graalpy-3.12 + +# Create a virtual environment with GraalPy +uv venv --python graalpy-3.12 +{%- endhighlight -%} +
+
+
+ + uv badge + +
+
+
+
+
+ {%- highlight yml -%} +steps: +- uses: actions/checkout@v4 +- uses: actions/setup-python@v5 + with: + python-version: 'graalpy-{{ site.language_version }}' +- run: python my_script.py + {%- endhighlight -%} +
+ +
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
VersionLinux (aarch64)Linux (amd64)macOS (aarch64)Windows (amd64)
+ {{ site.language_version }} + + download icon + + download icon + + download icon + + download icon +
Latest early access build + download icon + + download icon + + download icon + + download icon +
+
+
+ download icon +
+
+
+
+
+
+ + +
+ +
diff --git a/docs/site/02-downloads.md b/docs/site/02-downloads.md deleted file mode 100644 index e6a9b17d16..0000000000 --- a/docs/site/02-downloads.md +++ /dev/null @@ -1,339 +0,0 @@ ---- -layout: base -title: Downloads -permalink: downloads/ -published: false ---- - -
-
-
-
-

Download GraalPy from Maven Central

-
-
- Have a Java application? -
-
-You can extend it with Python code or leverage packages from the Python ecosystem. GraalPy is available on Maven Central and can be added as a dependency to your Maven or Gradle project as — see setup instructions. -
-
-
-
-
-
- -
-
-
-
-

Download Standalone Distributions of GraalPy

-
-
- Do you want to test your Python application or package on GraalPy? -
-
- To test Python code on GraalPy, a standalone distribution is available for different platforms and in two different kinds: Native (for compact download and footprint) and JVM (for full Java interoperability). We recommend the distributions based on Oracle GraalVM for best performance and advanced features (released under the GFTC license). Distributions based on GraalVM Community Edition (released under the OSI-approved UPL license) are available on GitHub. Standalone distributions are also available via pyenv, pyenv-win, and setup-python: -
-
-
-
-
- {%- highlight bash -%} -# Latest GraalPy release -pyenv install graalpy-{{ site.language_version }} -pyenv shell graalpy-{{ site.language_version }} - -# Latest EA build of GraalPy -pyenv install graalpy-dev -pyenv shell graalpy-dev - {%- endhighlight -%} -
-
-
- pyenv and pyenv-win -
-
-
-
-
- {%- highlight yml -%} -steps: -- uses: actions/checkout@v4 -- uses: actions/setup-python@v5 - with: - python-version: 'graalpy-{{ site.language_version }}' -- run: python my_script.py - {%- endhighlight -%} -
- -
-
- -
GraalPy on Oracle GraalVM is free to use in production and free to redistribute, at no cost, under the GraalVM Free Terms and Conditions.
-
-
-
-
diff --git a/docs/site/02-jvm-developers-docs.md b/docs/site/02-jvm-developers-docs.md new file mode 100644 index 0000000000..8c969c3572 --- /dev/null +++ b/docs/site/02-jvm-developers-docs.md @@ -0,0 +1,39 @@ +--- +layout: docs +permalink: jvm-developers/docs/ +audience_identifier: jvm +title: Documentation +--- + +# Documentation for JVM Developers + +**For JVM developers who need to use Python libraries from their JVM applications or migrate from legacy Jython code.** + +You do not need to install GraalPy separately - you can use GraalPy directly in Java with Maven or Gradle. +This lets you call Python libraries like NumPy, pandas, or any PyPI package from your Java application. +GraalPy also provides a migration path from Jython 2.x to Python 3.x with better performance and maintained Java integration capabilities. + +{% gfm_docs ../user/Version-Compatibility.md %} + +{% gfm_docs ../user/Platform-Support.md %} + +These guides cover everything you need to know: + +{% gfm_docs ../user/Embedding-Getting-Started.md %} +{% gfm_docs ../user/Embedding-Build-Tools.md %} +{% gfm_docs ../user/Embedding-Permissions.md %} +{% gfm_docs ../user/Embedding-Native-Extensions.md %} +{% gfm_docs ../user/Interoperability.md %} +{% gfm_docs ../user/Native-Images-with-Python.md %} +{% gfm_docs ../user/Python-on-JVM.md %} + +

+Python Context Options +

+Below are the options you can set on contexts for GraalPy. +{% python_options ../../graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java %} + +{% gfm_docs ../user/Test-Tiers.md %} +{% gfm_docs ../user/Troubleshooting.md %} + +{% copy_assets ../user/assets %} diff --git a/docs/site/02-python-developers-docs.md b/docs/site/02-python-developers-docs.md new file mode 100644 index 0000000000..be9e34bc9e --- /dev/null +++ b/docs/site/02-python-developers-docs.md @@ -0,0 +1,30 @@ +--- +layout: docs +permalink: python-developers/docs/ +audience_identifier: python +title: Documentation +--- + +# Documentation for Python Developers + +**You want to use GraalPy instead of the standard Python from python.org.** + +Install GraalPy on your machine and use it like any Python interpreter. +You get better performance, the ability to compile to native binaries, and access to the GraalVM ecosystem. + +{% gfm_docs ../user/Version-Compatibility.md %} + +{% gfm_docs ../user/Platform-Support.md %} + +These guides cover everything you need to know: + +{% gfm_docs ../user/Standalone-Getting-Started.md %} +{% gfm_docs ../user/Python-Standalone-Applications.md %} +{% gfm_docs ../user/Native-Extensions.md %} +{% gfm_docs ../user/Interoperability.md %} +{% gfm_docs ../user/Performance.md %} +{% gfm_docs ../user/Tooling.md %} + +{% gfm_docs ../user/Test-Tiers.md %} + +{% copy_assets ../user/assets %} diff --git a/docs/site/03-compatibility.md b/docs/site/03-jvm-developers-compatibility.md similarity index 98% rename from docs/site/03-compatibility.md rename to docs/site/03-jvm-developers-compatibility.md index 75c5f4dd50..5c4267791b 100644 --- a/docs/site/03-compatibility.md +++ b/docs/site/03-jvm-developers-compatibility.md @@ -1,7 +1,8 @@ --- layout: base +permalink: jvm-developers/compatibility/ +audience_identifier: jvm title: Compatibility -permalink: compatibility/ --- @@ -164,7 +166,7 @@ img.pylogo { }); var module_testing_csv = new Promise(function (resolve, reject) { const xhr = new XMLHttpRequest(); - const url = `/python/module_results/python-module-testing-${graalpyVersion}.csv`; + const url = `{{ '/module_results/' | relative_url }}python-module-testing-${graalpyVersion}.csv`; xhr.open('GET', url); xhr.overrideMimeType('text/plain'); xhr.onload = function () { @@ -181,7 +183,7 @@ img.pylogo { }); var wheels_csv = new Promise(function (resolve, reject) { const xhr = new XMLHttpRequest(); - const url = `/python/wheels/${graalpyVersion}.csv`; + const url = `{{ '/wheels/' | relative_url }}${graalpyVersion}.csv`; xhr.open('GET', url); xhr.overrideMimeType('text/plain'); xhr.onload = function () { @@ -574,7 +576,7 @@ img.pylogo {
- info icon + info icon
Many more Python packages work on GraalPy than are listed here. If there is a package you are interested in that is not included, chances are that it might just work. If it does not, feel free to create an issue for us on GitHub.
diff --git a/docs/site/03-python-developers-compatibility.md b/docs/site/03-python-developers-compatibility.md new file mode 100644 index 0000000000..0ba5cc70e4 --- /dev/null +++ b/docs/site/03-python-developers-compatibility.md @@ -0,0 +1,626 @@ +--- +layout: base +permalink: python-developers/compatibility/ +audience_identifier: python +title: Compatibility +--- + + + + + + + +
+
+
+
+

Package Compatibility

+

GraalPy is compatible with many packages for data science and machine learning, including the popular PyTorch, NumPy, and Huggingface Transformers.

+
+
+
+
+ +
+
+
+
+
+
+ +
+

Numeric Computing

+
+
+
We test NumPy across multiple versions and know of multiple deployments where it brings numeric computing to Java.
+
+
+
+ +
+

Scientific Computing

+
+
+
SciPy's rich library for scientific computing is just a package download away.
+
+
+
+ +
+

Data Processing

+
+
+
Thanks to Arrow, Pandas on GraalPy can run multi-threaded while avoiding unneccessary data copies.
+
+
+
+
+
+ +
+

Models for any Task

+
+
+
The Huggingface transformers library works on GraalPy with its huge library of language, vision, and audio models.
+
+
+
+ +
+

Training and Inference

+
+
+
Train models and run inference on GraalPy with PyTorch, taking full advantage of the latest techniques and accellerator hardware.
+
+
+
+ +
+

Agentic Workflows

+
+
+
With Autogen and GraalPy you can write agentic workflows and use Java code to create tools for AI Agents.
+
+
+
+
+
+
+
+ +
+
+
+
+
+

Compatibility per GraalPy Release

+
+
+

GraalPy 25.0

+

GraalPy 24.2

+

GraalPy 24.1

+
+
+
+

Over 600 Python packages tested for compatibility with GraalPy

+
+
+
+
+
+
Compatible: loading...
+
+ Currently Untested: loading...
+
+
+
Currently + Incompatible: loading...
+
Not + Supported: loading...
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ info icon +
Many more Python packages work on GraalPy than are listed here. If there is a package you are interested in that is not included, chances are that it might just work. If it does not, feel free to create an issue for us on GitHub.
+
+
+ +
+
+ + +
+
+
+
+ + + + + + + + + + +
Python Packages
NameVersionNotes
+
+
+
+ + +
+
+ + +
+
+
+
+
+
+
diff --git a/docs/site/CNAME b/docs/site/CNAME new file mode 100644 index 0000000000..4cfdd3e7eb --- /dev/null +++ b/docs/site/CNAME @@ -0,0 +1 @@ +graalpy.org diff --git a/docs/site/Gemfile b/docs/site/Gemfile index 954472b169..51c5774c7f 100644 --- a/docs/site/Gemfile +++ b/docs/site/Gemfile @@ -2,7 +2,7 @@ source ENV.fetch("RUBYGEMS_MIRROR", "https://rubygems.org") gem "jekyll", "~> 4.3.4" -gem "graal-languages-jekyll-theme" +gem "graal-languages-jekyll-theme", git: "https://github.com/graalvm/graal-languages-jekyll-theme.git", branch: "main" group :jekyll_plugins do gem "jekyll-relative-links" diff --git a/docs/site/Gemfile.lock b/docs/site/Gemfile.lock index d7ae358a10..7956bf721d 100644 --- a/docs/site/Gemfile.lock +++ b/docs/site/Gemfile.lock @@ -1,60 +1,74 @@ +GIT + remote: https://github.com/graalvm/graal-languages-jekyll-theme.git + revision: 6ae97469860513f340ee832c7b84be81c43d52ad + branch: main + specs: + graal-languages-jekyll-theme (0.2.0) + jekyll (~> 4.3) + GEM remote: https://rubygems.org/ specs: Ascii85 (2.0.1) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) - afm (0.2.2) - async (2.23.0) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) + afm (1.0.0) + async (2.37.0) console (~> 1.29) fiber-annotation - io-event (~> 1.9) + io-event (~> 1.11) metrics (~> 0.12) - traces (~> 0.15) - bigdecimal (3.1.8) + traces (~> 0.18) + benchmark (0.5.0) + bigdecimal (3.3.1) coderay (1.1.3) colorator (1.1.0) - concurrent-ruby (1.3.4) - console (1.29.3) + concurrent-ruby (1.3.6) + console (1.34.3) fiber-annotation fiber-local (~> 1.1) json - csv (3.3.0) + csv (3.3.5) em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) - ethon (0.16.0) + ethon (0.18.0) ffi (>= 1.15.0) + logger eventmachine (1.2.7) - ffi (1.17.0-x86_64-linux-gnu) + ffi (1.17.4-arm64-darwin) + ffi (1.17.4-x86_64-linux-gnu) fiber-annotation (0.2.0) fiber-local (1.1.0) fiber-storage - fiber-storage (1.0.0) + fiber-storage (1.0.1) forwardable-extended (2.6.0) - google-protobuf (4.28.3-x86_64-linux) + google-protobuf (4.35.0-arm64-darwin) bigdecimal - rake (>= 13) - graal-languages-jekyll-theme (0.1.0) - jekyll (~> 4.3) + rake (~> 13.3) + google-protobuf (4.35.0-x86_64-linux-gnu) + bigdecimal + rake (~> 13.3) hashery (2.1.2) - html-proofer (5.0.10) + html-proofer (5.2.1) addressable (~> 2.3) async (~> 2.1) + benchmark (~> 0.5) nokogiri (~> 1.13) pdf-reader (~> 2.11) rainbow (~> 3.0) typhoeus (~> 1.3) yell (~> 2.0) zeitwerk (~> 2.5) - http_parser.rb (0.8.0) - httparty (0.22.0) + http_parser.rb (0.8.1) + httparty (0.24.2) csv mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) - i18n (1.14.6) + i18n (1.14.8) concurrent-ruby (~> 1.0) - io-event (1.9.0) + io-console (0.8.2) + io-event (1.11.2) jekyll (4.3.4) addressable (~> 2.4) colorator (~> 1.0) @@ -73,75 +87,85 @@ GEM webrick (~> 1.7) jekyll-relative-links (0.7.0) jekyll (>= 3.3, < 5.0) - jekyll-sass-converter (3.0.0) - sass-embedded (~> 1.54) - jekyll-seo-tag (2.8.0) + jekyll-sass-converter (3.1.0) + sass-embedded (~> 1.75) + jekyll-seo-tag (2.9.0) jekyll (>= 3.8, < 5.0) jekyll-watch (2.2.1) listen (~> 3.0) - json (2.10.1) - kramdown (2.4.0) - rexml + json (2.19.5) + kramdown (2.5.2) + rexml (>= 3.4.4) kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liquid (4.0.4) - listen (3.9.0) + listen (3.10.0) + logger rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) + logger (1.7.0) mercenary (0.4.0) method_source (1.1.0) - metrics (0.12.1) + metrics (0.15.0) mini_mime (1.1.5) - multi_xml (0.7.1) - bigdecimal (~> 3.1) - nokogiri (1.18.3-x86_64-linux-gnu) + multi_xml (0.9.1) + bigdecimal (>= 3.1, < 5) + nokogiri (1.19.3-arm64-darwin) + racc (~> 1.4) + nokogiri (1.19.3-x86_64-linux-gnu) racc (~> 1.4) pathutil (0.16.2) forwardable-extended (~> 2.6) - pdf-reader (2.14.1) + pdf-reader (2.15.1) Ascii85 (>= 1.0, < 3.0, != 2.0.0) - afm (~> 0.2.1) + afm (>= 0.2.1, < 2) hashery (~> 2.0) ruby-rc4 ttfunk - pry (0.14.2) + pry (0.16.0) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (6.0.1) + reline (>= 0.6.0) + public_suffix (7.0.5) racc (1.8.1) - rack (3.1.8) + rack (3.2.6) rainbow (3.1.1) - rake (13.2.1) + rake (13.4.2) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.3.9) - rouge (4.5.1) + reline (0.6.3) + io-console (~> 0.5) + rexml (3.4.4) + rouge (4.7.0) ruby-rc4 (0.1.5) safe_yaml (1.0.5) - sass-embedded (1.81.0-x86_64-linux-gnu) - google-protobuf (~> 4.28) + sass-embedded (1.100.0-arm64-darwin) + google-protobuf (~> 4.31) + sass-embedded (1.100.0-x86_64-linux-gnu) + google-protobuf (~> 4.31) siteleaf (2.3.0) httparty (>= 0.16.0) jekyll (>= 1.4.1) rack terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - traces (0.15.2) + traces (0.18.2) ttfunk (1.8.0) bigdecimal (~> 3.1) - typhoeus (1.4.1) - ethon (>= 0.9.0) + typhoeus (1.6.0) + ethon (>= 0.18.0) unicode-display_width (2.6.0) - webrick (1.9.0) + webrick (1.9.2) yell (2.2.2) - zeitwerk (2.7.2) + zeitwerk (2.7.5) PLATFORMS + arm64-darwin-24 x86_64-linux DEPENDENCIES - graal-languages-jekyll-theme + graal-languages-jekyll-theme! html-proofer http_parser.rb (~> 0.6.0) jekyll (~> 4.3.4) diff --git a/docs/site/_config.yml b/docs/site/_config.yml index 51beefacf6..76f3a59edf 100644 --- a/docs/site/_config.yml +++ b/docs/site/_config.yml @@ -1,11 +1,17 @@ -baseurl: "/python" -url: "https://graalvm.org" +baseurl: "" +url: "https://graalpy.org" github: "oracle/graalpython" -language_version: 25.0.2 +language_version: 25.0.3 +python_version: 3.12 name: GraalPy - permalink: pretty +audiences: + - identifier: jvm + name: JVM + - identifier: python + name: Python + plugins: - jekyll-relative-links - jekyll-seo-tag diff --git a/docs/site/_plugins/graalpy_wheels.txt b/docs/site/_plugins/graalpy_wheels.txt index f9539d9d77..e4c9a544d1 100644 --- a/docs/site/_plugins/graalpy_wheels.txt +++ b/docs/site/_plugins/graalpy_wheels.txt @@ -71,9 +71,9 @@ numpy-2.2.4-graalpy312-graalpy250_312_native-macosx_13_0_arm64.whl numpy-2.2.4-graalpy312-graalpy250_312_native-manylinux_2_27_aarch64.whl numpy-2.2.4-graalpy312-graalpy250_312_native-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl numpy-2.2.4-graalpy312-graalpy250_312_native-win_amd64.whl -oracledb-3.0.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl -oracledb-3.0.0-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl -oracledb-3.0.0-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl +oracledb-3.4.2-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl +oracledb-3.4.2-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl +oracledb-3.4.2-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl polyleven-0.8-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl polyleven-0.8-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux_2_5_x86_64.whl polyleven-0.8-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl diff --git a/docs/site/assets/img/python/info-icon.svg b/docs/site/assets/img/python/info-icon.svg deleted file mode 100644 index cd36065be5..0000000000 --- a/docs/site/assets/img/python/info-icon.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/docs/site/assets/img/python/pyenv-badge.svg b/docs/site/assets/img/python/pyenv-badge.svg new file mode 100644 index 0000000000..ec1dc8f212 --- /dev/null +++ b/docs/site/assets/img/python/pyenv-badge.svg @@ -0,0 +1,10 @@ + + + + + + pyenv + + + + diff --git a/docs/site/assets/img/python/uv-badge.svg b/docs/site/assets/img/python/uv-badge.svg new file mode 100644 index 0000000000..685deac379 --- /dev/null +++ b/docs/site/assets/img/python/uv-badge.svg @@ -0,0 +1,10 @@ + + + + + + uv + + + + diff --git a/docs/site/docs-redirect.md b/docs/site/docs-redirect.md new file mode 100644 index 0000000000..efb05173c2 --- /dev/null +++ b/docs/site/docs-redirect.md @@ -0,0 +1,15 @@ +--- +layout: docs +permalink: docs/ +redirect_to: jvm-developers/docs/ +--- + + + + + +

Redirecting to the documentation for JVM Developers...

diff --git a/docs/site/index.md b/docs/site/index.md index b90a4e23fb..78cde640b4 100644 --- a/docs/site/index.md +++ b/docs/site/index.md @@ -1,20 +1,14 @@ --- layout: base --- -
+
+
-

A high-performance embeddable Python 3 runtime for Java

- -
-
- Python icon +

A high-performance embeddable Python 3 runtime

@@ -22,386 +16,57 @@ layout: base
- -
+
-
-
-

Benefits

-
-
-
- access icon -
-
-

Python for Java

-
- -
-
-
- compatibility icon -
-
-

Python 3 Compatible

-
-
-
Compatible with many Python packages, including popular AI and Data Science libraries
-
-
-
-
- speed icon -
-
-

Fastest Python on the JVM

-
-
-
Graal JIT compiles Python for native code speed
-
-
-
-
-
-
- upgrade icon -
-
-

Modern Python for the JVM

-
-
-
GraalPy provides an upgrade path for Jython users
-
-
-
-
- code icon -
-
-

Script Java with Python

-
-
-
Extend applications with Python scripts that interact with Java
-
-
-
-
- binary icon -
-
-

Simple distribution

-
-
-
Package Python applications as a single binary with GraalVM Native Image
-
-
-
-
-
-
-
- - -
-
-
-
-

How to Get Started

-
-
You have the option to extend your Java application with Python, or go straight to the starter project
-
-
-
-

1. Add GraalPy as a dependency from Maven Central

-
-
-
-
-

1. Add GraalPy as a dependency from Maven Central

-
- {%- highlight xml -%} - - org.graalvm.polyglot - polyglot - {{ site.language_version }} - - - org.graalvm.polyglot - python - {{ site.language_version }} - pom - - {%- endhighlight -%} -
-
- -
-
-
-
-
-

or

-
- {%- highlight groovy -%} -implementation("org.graalvm.polyglot:polyglot:{{ site.language_version }}") -implementation("org.graalvm.python:python-embedding:{{ site.language_version }}") - {%- endhighlight -%} -
-
- -
-
-
-

2. Embed Python code in Java

-
-
-
-
-

2. Embed Python code in Java

-
- {%- highlight java -%} +
+
+
+ +

Embed Python in JVM Applications

+
+{%- highlight java -%} import org.graalvm.polyglot.Context; -import org.graalvm.python.embedding.GraalPyResources; -try (Context context = GraalPyResources.contextBuilder().build()) { +try (Context context = Context.newBuilder().build()) { context.eval("python", "print('Hello from GraalPy!')"); } - {%- endhighlight -%} -
-
- -
-
-
-

3. Add GraalPy plugins for additional Python packages (optional)

-
-
-
-
-

3. Add GraalPy plugins for additional Python packages (optional)

-
- {%- highlight xml -%} - - org.graalvm.python - graalpy-maven-plugin - {{ site.language_version }} - - - - - - pyfiglet==1.0.2 - - - - process-graalpy-resources - - - - - {%- endhighlight -%} -
-
- -
-
-
-
-
-

or

-
- {%- highlight groovy -%} -plugins { - id("org.graalvm.python") version "{{ site.language_version }}" -} - -graalPy { - packages = setOf("pyfiglet==1.0.2") -} - {%- endhighlight -%} -
-
- -
-
-
-
-
-
-
- -
- -
- -
-
-
-
-

Demos

- +
    +
  • Use Python packages directly in Java, Kotlin, or Scala
  • +
  • Upgrade Jython projects to Python 3
  • +
  • Control permissions for Python code from full host access to fully sandboxed
  • +
  • Script JVM applications with Python
  • +
-
-
-
-
+
+ +

Build and Run Python Applications

+
+{%- highlight bash -%} +$ pyenv install graalpy-{{ site.language_version }} +$ pyenv shell graalpy-{{ site.language_version }} - -
-
-
-
-

Videos

-
- -
-
-

Tips and Tricks for GraalVM and Graal Languages

-
-
-
In this session, Fabio Niephaus from the GraalVM team shows his favourite tips and tricks for using GraalPy and other Graal Languages in IntelliJ IDEA. He also shows how to use IntelliJ IDEA as a multi-language IDE. Language injections and support for various debugging protocols make it easy to embed and debug code written in languages like Python in Java applications. -
-
-
-
-
- -
-
-

Supercharge your Java Applications with Python!
Jfokus'25

-
-
-
Projects such as LangChain4j, Spring AI, and llama3.java got the Java community very excited about AI in the last year. - The Python ecosystem also provides many powerful packages for data science, machine learning, and more. - Wouldn't it be cool if you, as a Java developer, could benefit from this, too? -
- In this talk, we show how you can get started with GraalPy and use packages from the Python ecosystem. - We also show some live demos and preview upcoming features that will improve the interaction between Java and native extensions that ship with popular Python packages. -
-
-
-
-
- -
-
-

Supercharge your Java Applications with Python!
Devoxx'24

-
-
-
The Python ecosystem provides many powerful packages for data science, machine learning, and more, that you can now leverage in Java. - Get started by adding GraalPy as a dependency to your Java project. - There are also Maven and Gradle plugins for GraalPy that help you install additional Python packages. - In this presentation, we also show live demos that illustrate different use cases, such as a Spring Boot application that visualizes data with Python, Python running on JBang!, a Java application scripted with Python, and more. -
-
-
+$ python3 -c "import sys; print(sys.implementation.name)" +graalpy +$ python3 -m timeit "'-'.join(str(n) for n in range(100))" +500000 loops, best of 5: 757 nsec per loop +{%- endhighlight -%} +
+
    +
  • Speed up Python applications with the Graal JIT
  • +
  • Compatible with many Python AI and data science packages
  • +
  • Package Python applications as a single binary
  • +
  • Use Java libraries in Python applications
  • +
+ + + +
diff --git a/docs/user/Embedding-Build-Tools.md b/docs/user/Embedding-Build-Tools.md index b43e463a3a..0526fb473b 100644 --- a/docs/user/Embedding-Build-Tools.md +++ b/docs/user/Embedding-Build-Tools.md @@ -1,6 +1,8 @@ # Embedding Build Tools -> **Note**: The GraalPy build tools are being developed [GraalPy Extensions repository](https://github.com/oracle/graalpy-extensions) on GitHub. +> **Note**: The GraalPy build tools are being developed in the [GraalPy Extensions repository](https://github.com/oracle/graalpy-extensions) on GitHub. + +This guide covers advanced configuration and deployment options for the GraalPy Maven and Gradle plugins. For quick start tutorials and basic setup, see [Embedding Python in Java](Embedding-Getting-Started.md). The GraalPy **Maven** and **Gradle** plugins simplify embedding Python in Java applications by automatically managing Python resources during your build process: @@ -8,7 +10,7 @@ The GraalPy **Maven** and **Gradle** plugins simplify embedding Python in Java a - **Third-party packages**: Python libraries (like NumPy, requests) automatically installed in the build according to your plugin configuration. These plugins handle the complexity of packaging Python code with your Java application, ensuring all dependencies are available at runtime but you need to configure your Java application to access them at runtime. -The [GraalPyResources](https://github.com/oracle/graalpy-extensions/blob/main/org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/GraalPyResources.java) API provides factory methods that create a preconfigured GraalPy Context. +The [GraalPyResources](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/GraalPyResources.html) API provides factory methods that create a preconfigured GraalPy Context. > The preconfigured GraalPy Context is a GraalVM Context that has been automatically set up with the right settings to access your Python resources without you having to manually configure all the details. @@ -21,29 +23,31 @@ You can choose between two deployment approaches: ### Virtual Filesystem -With the Virtual Filesystem approach, your Python resources are embedded directly inside your JAR file or Native Image executable as standard Java resources. This creates a self-contained application with everything bundled together. +With the Virtual Filesystem approach, your Python resources are embedded directly inside your JAR file or Native Image executable as standard Java resources. +This creates a self-contained application with everything bundled together. This approach involves the following steps: - Python files are packaged as Java resources in dedicated resource directories (like `src/main/resources`) - Multiple resource directories are merged during the build process by Maven or Gradle - You can configure the Java resource path (default: `org.graalvm.python.vfs`) that gets mapped to a Virtual Filesystem mount point in Python (default: `/graalpy_vfs`) -- GraalPy's Virtual Filesystem transparently maps these resources to Python file paths +- GraalPy's Virtual Filesystem transparently maps these resources to Python file paths - Your Python code can use normal file operations (`open()`, `import`, etc.) without knowing the files are embedded For example, a file at `src/main/resources/org.graalvm.python.vfs/src/mymodule.py` becomes accessible to Python as `/graalpy_vfs/src/mymodule.py`. You can customize the resource path (default: `org.graalvm.python.vfs`) and mount point (default: `/graalpy_vfs`) to avoid conflicts with other libraries. -To use the Virtual Filesystem in your Java application, use the factory methods in the [GraalPyResources](https://github.com/oracle/graalpy-extensions/blob/main/org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/GraalPyResources.java) API: +To use the Virtual Filesystem in your Java application, use the factory methods in the [GraalPyResources](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/GraalPyResources.html) API: - `GraalPyResources.createContext()` - Creates a ready-to-use context with default Virtual Filesystem configuration -- `GraalPyResources.contextBuilder()` - Returns a context builder for additional customization before creating the context +- `GraalPyResources.contextBuilder()` - Returns a context builder for additional customization before creating the context - `GraalPyResources.contextBuilder(VirtualFileSystem)` - Returns a context builder with a custom Virtual Filesystem configuration #### Java Resource Path -When building reusable libraries, use a unique Java resource path to prevent conflicts with other Virtual Filesystem users. This ensures your library's Python resources don't interfere with other libraries on the classpath. +When building reusable libraries, use a unique Java resource path to prevent conflicts with other Virtual Filesystem users. +This ensures your library's Python resources don't interfere with other libraries on the classpath. The recommended path is: ```bash @@ -56,21 +60,26 @@ This path must be configured identically in both your build plugin and runtime c #### Extracting files from Virtual Filesystem -Some files need to exist on the real filesystem rather than staying embedded as Java resources. +Some files need to exist on the real filesystem rather than staying embedded as Java resources. + +This is required for Python C extensions (`.so`, `.dylib`, `.pyd`, `.dll`) and other files that are opened directly by native libraries outside Python's file APIs. +GraalPy automatically extracts common native library and font file types such as `.so`, `.dylib`, `.pyd`, `.dll`, and `.ttf` when first accessed, then delegates to the real files for subsequent operations. -This is required for Python C extensions (`.so`, `.dylib`, `.pyd`, `.dll`) and font files (`.ttf`) that must be accessed by the operating system loader outside the Truffle sandbox. -GraalPy automatically extracts these file types to a temporary directory when first accessed, then delegates to the real files for subsequent operations. +If a package reports that an embedded file under `/graalpy_vfs` does not exist, check whether a native library is opening that file directly. +For example, packages using ONNX Runtime may need model files such as `.onnx` to be extracted as real files. -Use the `VirtualFileSystem$Builder#extractFilter` API to modify which files get extracted automatically. For full control, extract all resources to a user-defined directory before creating your GraalPy context: +Use the `VirtualFileSystem$Builder#extractFilter` API to customize which files are extracted automatically. +For full control, extract all resources to a user-defined directory before creating your GraalPy context: - `GraalPyResources.extractVirtualFileSystemResources(VirtualFileSystem vfs, Path externalResourcesDirectory)` - Extract resources to a specified directory - `GraalPyResources.contextBuilder(Path externalResourcesDirectory)` - Create a context builder using the extracted resources directory -For more information, see [GraalPyResources](https://github.com/oracle/graalpy-extensions/blob/main/org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/GraalPyResources.java). +For more information, see [GraalPyResources](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/GraalPyResources.html). ### External Directory -With the External Directory approach, your Python resources are stored in a separate directory on the filesystem rather than being embedded as Java resources. This creates a deployment where Python files exist as regular files that you must distribute alongside your application. +With the External Directory approach, your Python resources are stored in a separate directory on the filesystem rather than being embedded as Java resources. +This creates a deployment where Python files exist as regular files that you must distribute alongside your application. This approach involves the following steps: @@ -80,11 +89,11 @@ This approach involves the following steps: - Smaller JAR/executable size since Python resources aren't embedded To use an external directory, create your GraalPy context with: -- `GraalPyResources.createContextBuilder(Path)` - Creates a context builder pointing to your external directory path +- `GraalPyResources.contextBuilder(Path)` - Creates a context builder pointing to your external directory path ## Directory Structure -The [GraalPyResources](https://github.com/oracle/graalpy-extensions/blob/main/org.graalvm.python.embedding/src/main/java/org/graalvm/python/embedding/GraalPyResources.java) factory methods rely on this directory structure, which includes a standard [Python virtual environment](https://docs.python.org/3.11/tutorial/venv.html) in the `venv` subdirectory: +The [GraalPyResources](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/GraalPyResources.html) factory methods rely on this directory structure, which includes a standard [Python virtual environment](https://docs.python.org/3.11/tutorial/venv.html) in the `venv` subdirectory: | Directory | Purpose | Management | Python Path | | --------------- | ---------------------------- | ------------------ |------------ | @@ -100,20 +109,23 @@ The GraalPy Context is automatically configured to run within this virtual envir > **Important**: Plugin completely manages `venv/` - any manual changes will be overridden during builds. -## Python Dependency Management for Reproducible Builds +## Dependency Management Python packages typically specify dependencies as version ranges (e.g., `B>=2.0.0`) rather than fixed versions. This means today's build might install `B==2.0.0`, but tomorrow's clean build could pull the newly released `B==2.0.1`, potentially introducing breaking changes or GraalPy incompatibilities. ### Locking Dependencies -**We highly recommend locking all Python dependencies** when packages change. Run a Maven goal or Gradle task to generate _graalpy.lock_, which captures exact versions of all dependencies (those specified explicitly in the _pom.xml_ or _build.gradle_ files and all their transitive dependencies). +**We highly recommend locking all Python dependencies** when packages change. +Run a Maven goal or Gradle task to generate _graalpy.lock_, which captures exact versions of all dependencies (those specified explicitly in the _pom.xml_ or _build.gradle_ files and all their transitive dependencies). -Commit the _graalpy.lock_ file to version control (e.g., git). Once this file exists, Maven or Gradle builds will install the exact same package versions captured in the _graalpy.lock_ file. +Commit the _graalpy.lock_ file to version control (e.g., git). +Once this file exists, Maven or Gradle builds will install the exact same package versions captured in the _graalpy.lock_ file. If you modify dependencies in _pom.xml_ or _build.gradle_ and they no longer match what's in _graalpy.lock_, the build will fail and the user will be asked to explicitly regenerate the _graalpy.lock_ file. -We recommend specifying dependencies without version numbers in the _pom.xml_ or _build.gradle_ file. GraalPy automatically installs compatible versions for well-known packages. +We recommend specifying dependencies without version numbers in the _pom.xml_ or _build.gradle_ file. +GraalPy automatically installs compatible versions for well-known packages. Once installed, lock these versions to ensure reproducible builds. @@ -123,7 +135,8 @@ For information on the specific Maven or Gradle lock packages actions, see the [ ## GraalPy Maven Plugin -The GraalPy Maven Plugin automates Python resource management in Maven-based Java projects. It downloads Python packages, creates virtual environments, and configures deployment for both Virtual Filesystem (embedded) and External Directory approaches. +The GraalPy Maven Plugin automates Python resource management in Maven-based Java projects. +It downloads Python packages, creates virtual environments, and configures deployment for both Virtual Filesystem (embedded) and External Directory approaches. ### Maven Plugin Configuration @@ -147,11 +160,11 @@ Add the plugin configuration to your _pom.xml_ file: termcolor==2.2 - + GRAALPY-VFS/${project.groupId}/${project.artifactId} - + ${basedir}/python-resources @@ -172,12 +185,12 @@ allowing full use of pip's native dependency format. ``` > **Important:** You must configure either `packages` or `requirementsFile`, but not both. -> +> > When `requirementsFile` is used: > - the GraalPy lock file is **not created and not used** > - the `lock-packages` goal is **disabled** > - dependency locking must be handled externally by pip (for example using `pip freeze`) -> +> > Mixing `packages` and `requirementsFile` in the same configuration is not supported. #### Excluding Build-Only Packages @@ -218,12 +231,12 @@ To customize the lock file path, configure _graalPyLockFile_ : > **Note:** This only changes the path (defaults to _${basedir}/graalpy.lock_). To generate the lock file, run the `lock-packages` goal. -For more information of this feature, please see the -[Python Dependency Management for Reproducible Builds](#python-dependency-management-for-reproducible-builds) section. +For more information about dependency locking, see the [Dependency Management](#dependency-management) section. ## GraalPy Gradle Plugin -The GraalPy Gradle Plugin automates Python resource management in Gradle-based Java projects. It downloads Python packages, creates virtual environments, and configures deployment for both Virtual Filesystem (embedded) and External Directory approaches. +The GraalPy Gradle Plugin automates Python resource management in Gradle-based Java projects. +It downloads Python packages, creates virtual environments, and configures deployment for both Virtual Filesystem (embedded) and External Directory approaches. ### Gradle Plugin Configuration @@ -239,17 +252,17 @@ Add the plugin configuration to your _build.gradle_ file: ```groovy plugins { - id 'org.graalvm.python' version '25.0.2' + id 'org.graalvm.python' version '25.0.3' } graalPy { // Python packages (pip-style syntax) packages = ["termcolor==2.2"] - + // Choose ONE deployment approach: // Virtual Filesystem (embedded) resourceDirectory = "GRAALPY-VFS/my.group.id/artifact.id" - + // OR External Directory (separate files) externalDirectory = file("$rootDir/python-resources") } @@ -281,10 +294,9 @@ To customize the lock file path, configure _graalPyLockFile_: > **Note:** This only changes the path (defaults to _$rootDir/graalpy.lock_). To generate the lock file, run the `graalPyLockPackages` task. -For more information of this feature, please see the -[Python Dependency Management for Reproducible Builds](#python-dependency-management-for-reproducible-builds) section. +For more information about dependency locking, see the [Dependency Management](#dependency-management) section. -## Related Documentation +### Related Documentation - [Embedding Graal languages in Java](https://www.graalvm.org/reference-manual/embed-languages/) - [Permissions for Python Embeddings](Embedding-Permissions.md) diff --git a/docs/user/Embedding-Getting-Started.md b/docs/user/Embedding-Getting-Started.md new file mode 100644 index 0000000000..e97c96ec07 --- /dev/null +++ b/docs/user/Embedding-Getting-Started.md @@ -0,0 +1,265 @@ +# Embedding Python in Java + +This guide shows you how to embed Python code directly in Java applications using GraalPy. +You can use GraalPy with any JDK (GraalVM JDK, Oracle JDK, or OpenJDK). + +GraalPy provides dedicated Maven and Gradle plugins that handle all the complexity for you. +If you are using other build systems (Ant, Make, CMake), manual configuration is required. + +## Maven Quick Start + +The fastest way to get started is with GraalPy's Maven archetype, which generates a complete starter project for you. + +1. Generate a new project using the GraalPy Maven archetype: + + ```bash + mvn archetype:generate \ + -DarchetypeGroupId=org.graalvm.python \ + -DarchetypeArtifactId=graalpy-archetype-polyglot-app \ + -DarchetypeVersion=25.0.3 + ``` + + This generates the following project structure: + + ```bash + └── polyglot-app + ├── pom.xml + ├── src + │ └── main + │ ├── java + │ │ └── com + │ │ └── example + │ │ └── Main.java + │ └── resources + └── target/ + ``` + +2. Build a native executable using the [GraalVM `native-image` tool](https://www.graalvm.org/latest/reference-manual/native-image/) that was added for you automatically: + + ```bash + mvn -Pnative package + ``` + +3. Once completed, run the executable: + + ```bash + ./target/polyglot_app + ``` + + You should see "hello java" printed to the console. + +The generated project includes everything you need: the [GraalVM Polyglot API](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/package-summary.html) for Python execution, Python virtual environment management, and examples showing how to integrate Python packages. +The generated _pom.xml_ and Java code are well-documented with explanations of all features. + +For advanced plugin configuration, deployment options, and dependency management, see the [Embedding Build Tools](Embedding-Build-Tools.md) guide. + +### Cross-Platform Distribution + +For creating cross-platform JARs with native Python packages, see the [Virtual Filesystem deployment section](Embedding-Build-Tools.md#virtual-filesystem) in the Build Tools guide. + +## Gradle Quick Start + +If you prefer Gradle, here is how to set up a new project with GraalPy embedding: + +1. Create a new Java application with Gradle: + + ```bash + gradle init --type java-application \ + --project-name interop \ + --package interop \ + --no-split-project + ``` + + This generates the following project structure: + + ```bash + └── app + ├── build.gradle + └── src + └── main + ├── java + │ └── interop + │ └── App.java + └── resources + ``` + +2. Add GraalPy dependencies to your _app/build.gradle_ file. + - Include the GraalPy support and the [GraalVM Polyglot API](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/package-summary.html) in the `dependencies` section: + + ```gradle + implementation("org.graalvm.polyglot:polyglot:25.0.3") + implementation("org.graalvm.python:python-embedding:25.0.3") + ``` + +3. Replace the _App.java_ content with this simple Python embedding example: + + ```java + package interop; + + import org.graalvm.polyglot.*; + import org.graalvm.python.embedding.GraalPyResources; + + class App { + public static void main(String[] args) { + try (var context = GraalPyResources.createContext()) { + System.out.println(context.eval("python", "'Hello Python!'").asString()); + } + } + } + ``` + +4. Run the application with Gradle: + + ```bash + ./gradlew run + ``` + + The application prints "Hello Python!" to the console. + + > **Note**: GraalPy's performance depends on the JDK you are using. For optimal performance, see the [Runtime Optimization Support](https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support) guide. + +## Adding Python Dependencies + +To use third-party Python packages like NumPy or Requests in your embedded application: + +Configure the GraalPy Maven or Gradle plugin with the packages your application needs. +The plugin installs packages into a managed virtual environment, and `GraalPyResources` configures the Python context to import from it. + +### Maven + +Add the Python embedding dependency and GraalPy Maven plugin configuration to your _pom.xml_ file: + +```xml + + + org.graalvm.python + python-embedding + 25.0.3 + + + + + + + org.graalvm.python + graalpy-maven-plugin + 25.0.3 + + + + + termcolor==2.2 + + + + process-graalpy-resources + + + + + + +``` + +### Gradle + +Add the GraalPy Gradle plugin and configure dependencies in _app/build.gradle_: + +```gradle +plugins { + id "java" + id "application" + id "org.graalvm.python" version "25.0.3" +} + +dependencies { + implementation("org.graalvm.python:python-embedding:25.0.3") +} + +graalPy { + packages = ["termcolor==2.2"] +} +``` + +Then use the Python package from Java: + +```java +package interop; + +import org.graalvm.polyglot.*; +import org.graalvm.python.embedding.GraalPyResources; + +class App { + public static void main(String[] args) { + try (Context context = GraalPyResources.contextBuilder().build()) { + String src = """ + from termcolor import colored + colored_text = colored("hello java", "red", attrs=["reverse", "blink"]) + print(colored_text) + """; + context.eval("python", src); + } + } +} +``` + +For complete plugin configuration options, deployment strategies, and dependency management, see [Embedding Build Tools](Embedding-Build-Tools.md). + +## Other Build Systems (Ant, CMake, Makefile) + +If you are using build systems like Ant, Makefiles, or CMake that do not directly support Maven dependencies, you can still use GraalPy. +Projects like [Apache Ivy™](https://ant.apache.org/ivy/history/master/tutorial/start.html) can resolve Maven dependencies for these systems, you might prefer a simpler approach. +GraalPy provides a tool to download the required JAR files directly. + +### Manual Setup + +1. Set up your project structure with a directory for dependencies: + + ```bash + ├── lib/ # JAR dependencies + │ └── *.jar + └── src/ # Your Java source files + └── *.java + ``` + +2. Download GraalPy dependencies using the bundled tool: + + First, [install GraalPy](Standalone-Getting-Started.md#installation) and ensure `graalpy` is in your PATH. + + Then run the appropriate command for your system: + + **On Linux/macOS:** + + ```bash + export GRAALPY_HOME=$(graalpy -c 'print(__graalpython__.home)') + export GRAALPY_VERSION=$(graalpy -c 'import __graalpython__; print(__graalpython__.version)') + "${GRAALPY_HOME}/libexec/graalpy-polyglot-get" -a python -o lib -v "${GRAALPY_VERSION}" + ``` + + **On Windows (PowerShell):** + + ```powershell + $GRAALPY_HOME = graalpy -c "print(__graalpython__.home)" + $GRAALPY_VERSION = graalpy -c "import __graalpython__; print(__graalpython__.version)" + & "$GRAALPY_HOME/libexec/graalpy-polyglot-get" -a python -o lib -v "$GRAALPY_VERSION" + ``` + + This downloads all required GraalPy JARs into your _lib_ directory. + +3. Write your embedding code: + + ```java + import org.graalvm.polyglot.*; + + public class Hello { + public static void main(String[] args) { + try (var context = Context.newBuilder() + .option("engine.WarnInterpreterOnly", "false") + .build()) { + System.out.println(context.eval("python", "'Hello Python!'").asString()); + } + } + } + ``` + + Make sure your build system includes all JARs from the _lib_ directory in the classpath. diff --git a/docs/user/Embedding-Native-Extensions.md b/docs/user/Embedding-Native-Extensions.md new file mode 100644 index 0000000000..c413e66c6f --- /dev/null +++ b/docs/user/Embedding-Native-Extensions.md @@ -0,0 +1,52 @@ +# Embedding Limitations for Native Extensions + +Python native extensions run by default as native binaries, with full access to the underlying system. +This has a few implications: + +1. Native code is entirely unrestricted and can circumvent any security protections Truffle or the JVM may provide. +2. Native data structures are not subject to the Java GC and the combination of them with Java data structures may lead to increased memory pressure or memory leaks. +3. Native libraries generally cannot be loaded multiple times into the same process, and they may contain global state that cannot be safely reset. + +## Full Native Access + +The Context API allows you to set options such as `allowIO`, `allowHostAccess`, `allowThreads`, and more on created contexts. +To use Python native extensions on GraalPy, the `allowNativeAccess` option must be set to `true`, but this opens the door to full native access. +This means that while Python code may be denied access to the host file system, thread or subprocess creation, and more, the native extension is under no such restriction. + +## Java Virtual Threads + +Python native extensions are not compatible with Java virtual threads. +Native extensions commonly use native thread-local state, including CPython and extension-runtime state that cannot track Java virtual-thread scheduling. +If an embedded application may load or call native extensions, run that Python code on platform threads. +For example, server applications that use virtual threads for request handling should dispatch GraalPy calls that may use native extensions to a platform-thread executor. + +## Memory Management + +Python C extensions, like the CPython reference implementation, use reference counting for memory management. +This is fundamentally incompatible with JVM GCs. + +Java objects may end up being referenced from native data structures that the JVM cannot trace, so to avoid crashing, GraalPy keeps such Java objects strongly referenced. +To avoid memory leaks, GraalPy implements a cycle detector that regularly traces references between Java objects and native objects that have crossed between the two worlds and cleans up strong references that are no longer needed. + +On the other side, reference-counted native extension objects may end up being referenced from Java objects, and in this case GraalPy bumps their reference count to make them unreclaimable. +Any such references to native extension objects are registered with a `java.lang.ref.WeakReference`, and when the JVM GC has collected the owning Java object, the reference count of the native object is reduced again. + +Both of these mechanisms together mean there is additional delay between objects becoming unreachable and their memory being reclaimed when compared to the CPython implementation. +This can manifest in increased memory usage when running C extensions. +You can tweak the context options `python.BackgroundGCTaskInterval`, `python.BackgroundGCTaskThreshold`, and `BackgroundGCTaskMinimum` to mitigate this. +They control the minimum interval between cycle detections, how much RSS memory must have increased since the last time to trigger the cycle detector, and the absolute minimum RSS under which no cycle detection should be done. +You can also manually trigger the detector with the Python `gc.collect()` call. + +## Multi-Context and Native Libraries + +Using C extensions in multiple contexts is only possible on Linux for now, and many C extensions still have issues in this mode. +You should test your applications thoroughly if you want to use this feature. +There are many possibilities for native code to sidestep the library isolation through other process-wide global state, corrupting the state and leading to incorrect results or crashing. +The implementation also relies on `venv` to work, even if you are not using external packages. + +To support creating multiple GraalPy contexts that access native modules within the same JVM or Native Image, GraalPy isolates them from each other. +The current strategy for this is to copy the libraries and modify them such that the dynamic library loader of the operating system will isolate them. +To do this, all GraalPy contexts in the same process, not just those in the same engine, must set the `python.IsolateNativeModules` option to `true`. +You should test your applications thoroughly if you want to use this feature, as there are many possibilities for native code to sidestep the library isolation through other process-wide global state. + +For more details on this, see [our implementation details](https://github.com/oracle/graalpython/blob/master/docs/contributor/IMPLEMENTATION_DETAILS.md#c-extension-copying). diff --git a/docs/user/Embedding-Permissions.md b/docs/user/Embedding-Permissions.md index e8913a7eb7..316714b44c 100644 --- a/docs/user/Embedding-Permissions.md +++ b/docs/user/Embedding-Permissions.md @@ -9,12 +9,16 @@ GraalPy integrates with the [GraalVM Polyglot Sandbox](https://www.graalvm.org/l GraalPy exposes the operating system interface to Python scripts in a GraalPy-specific way. By default, all access is routed through Java interfaces, but some packages rely on POSIX API details and require direct native access. -Graal languages (implemented on the [Truffle framework](https://www.graalvm.org/latest/graalvm-as-a-platform/language-implementation-framework/)) typically implement system-related functions using the [Truffle abstraction layer](https://github.com/oracle/graal/blob/master/truffle/docs/README.md). This layer is OS-independent and provides extension points when embedding GraalPy or other Graal languages into Java applications. For example, see the [Truffle FileSystem service-provider](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html). +Graal languages (implemented on the [Truffle framework](https://www.graalvm.org/latest/graalvm-as-a-platform/language-implementation-framework/)) typically implement system-related functions using the [Truffle abstraction layer](https://www.graalvm.org/latest/graalvm-as-a-platform/language-implementation-framework/). +This layer is OS-independent and provides extension points when embedding GraalPy or other Graal languages into Java applications. +For example, see the [Truffle FileSystem service-provider](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html). -The standard Python library also provides an OS abstraction, but exposes lower level interfaces. For example, the `os` module directly exposes some POSIX functions. +The standard Python library also provides an OS abstraction, but exposes lower level interfaces. +For example, the `os` module directly exposes some POSIX functions. On non-POSIX platforms, this interface is emulated to a degree. -GraalPy provides two alternative implementations ("backends") of system-related functionality for built-in Python modules such as `os`. The `PosixModuleBackend` option determines which backend is used: `native` or `java`. +GraalPy provides two alternative implementations ("backends") of system-related functionality for built-in Python modules such as `os`. +The `PosixModuleBackend` option determines which backend is used: `native` or `java`. ### Native Backend @@ -22,7 +26,8 @@ The native backend directly calls the POSIX API in mostly the same way as CPytho This approach is the most compatible with CPython and provides bare access to the underlying OS interface without an intermediate emulation layer. -However, this implementation bypasses the Truffle abstraction layer by default. This means: +However, this implementation bypasses the Truffle abstraction layer by default. +This means: - **No sandboxing** protection - **No support** for custom [Truffle FileSystem service-provider](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/io/FileSystem.html) implementations - **No support** for other Polyglot API providers related to system interfaces @@ -38,19 +43,21 @@ Known limitations: ### Java Backend -The Java backend uses the [Truffle abstraction layer](https://github.com/oracle/graal/blob/master/truffle/docs/README.md), which provides: +The Java backend uses the [Truffle abstraction layer](https://www.graalvm.org/latest/graalvm-as-a-platform/language-implementation-framework/), which provides: - **Sandboxing support** for security - **Custom Polyglot API providers** for system interfaces - **Cross-platform compatibility** -Because this abstraction is POSIX-agnostic, it cannot expose all necessary functionality. Some functionality is emulated, and some is unsupported. +Because this abstraction is POSIX-agnostic, it cannot expose all necessary functionality. +Some functionality is emulated, and some is unsupported. -The Java backend is the default when [embedding GraalPy in Java applications](https://github.com/oracle/graal/blob/master/docs/reference-manual/embedding/embed-languages.md) using the `Context` API. +The Java backend is the default when [embedding GraalPy in Java applications](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/package-summary.html) using the `Context` API. #### Limitations of the Java Backend -To help identify compatibility issues, GraalPy can log information about known incompatibilities of functions executed at runtime. To enable this logging, use: `--log.python.compatibility.level=FINE` +To help identify compatibility issues, GraalPy can log information about known incompatibilities of functions executed at runtime. +To enable this logging, use: `--log.python.compatibility.level=FINE` Known limitations of the Java backend are: @@ -118,6 +125,7 @@ Context context = Context.newBuilder("python") ## Python Native Extensions -Python native extensions run by default as native binaries with full access to the underlying system. This means they bypass the security controls described above. +Python native extensions run by default as native binaries with full access to the underlying system. +This means they bypass the security controls described above. -For more information about limitations when embedding native extensions, see [Embedding limitations](Native-Extensions.md#embedding-limitations). \ No newline at end of file +For more information about limitations when embedding native extensions, see [Embedding limitations](Embedding-Native-Extensions.md). diff --git a/docs/user/Interoperability.md b/docs/user/Interoperability.md index 8283b43721..be5d011c21 100644 --- a/docs/user/Interoperability.md +++ b/docs/user/Interoperability.md @@ -2,11 +2,15 @@ GraalPy can interoperate with Java and other Graal languages that are implemented on the [Truffle framework](https://www.graalvm.org/latest/graalvm-as-a-platform/language-implementation-framework/). This means that you can use other languages' objects and functions directly from your Python scripts. -This interoperability works in both directions. Python can call other languages, and other languages can call Python code. +This interoperability works in both directions. +Python can call other languages, and other languages can call Python code. -## Interacting with Java from Python scripts +> **Note for Python users:** The Java interoperability sections below require a JVM-based GraalPy runtime with the relevant Java classes on the classpath. The later `polyglot` sections describe the more general cross-language APIs for working with other Graal languages. -Java is the host language of the JVM and runs the GraalPy interpreter itself. This means you can seamlessly access any Java class available in your classpath directly from Python. +## Call Java from Python + +Java is the host language of the JVM and runs the GraalPy interpreter itself. +This means you can seamlessly access any Java class available in your classpath directly from Python. ### Basic Java access @@ -63,9 +67,9 @@ assert java.is_function(my_list.add) assert java.instanceof(my_list, ArrayList) ``` -See the [Polyglot Programming](https://github.com/oracle/graal/blob/master/docs/reference-manual/polyglot-programming.md) and [Embed Languages](https://github.com/oracle/graal/blob/master/docs/reference-manual/embedding/embed-languages.md) documentation for more information about interoperability with other programming languages. +See the [Polyglot Programming](https://www.graalvm.org/latest/reference-manual/polyglot-programming/) and [Embed Languages](https://www.graalvm.org/latest/reference-manual/embedding/embed-languages/) documentation for more information about interoperability with other programming languages. -## Interacting with foreign objects from Python scripts +## Call Foreign Objects from Python When you use foreign objects in Python, GraalPy automatically makes them behave like their Python equivalents. @@ -116,12 +120,66 @@ assert l == [6] See the [Interop Types to Python](#interop-types-to-python) section for more interop traits and how they map to Python types. -## Interacting with other dynamic languages from Python scripts +## Passing Binary Data Between Java and Python + +Passing binary data between Java and Python deserves attention: + +- Java code typically uses `byte[]` or `java.nio.ByteBuffer` +- Python code typically uses `bytes`, `bytearray`, `memoryview`, or file-like APIs such as `io.BytesIO` + +### Java to Python + +Raw Java `byte[]` are accessible as `list`-like objects in Python. +Only integral values that fit into a signed `byte` can be read from or written to such objects. +Python, on the other hand, usually exposes binary data as unsigned byte values. +To achieve the equivalent of a "re-interpreting cast", Java byte arrays should be passed to Python using `ByteBuffer.wrap(byte[])`: + +```java +import java.nio.ByteBuffer; +byte[] data = ...; +ByteBuffer buffer = ByteBuffer.wrap(data); // does not copy +context.getBindings("python").putMember("java_buffer", buffer); +``` + +Python can then use the object through buffer-oriented binary data APIs: + +```python +memoryview(java_buffer) # does not copy +bytes(java_buffer) # copies into an immutable Python-owned buffer +bytearray(java_buffer) # copies into a mutable Python-owned buffer +io.BytesIO(java_buffer) # copies into BytesIO's internal storage +``` + +### Python to Java + +Python `bytes` and other bytes-like objects can be interpreted like any `java.lang.List`. +Because Python bytes are usually unsigned, however, they cannot simply be converted via `Value#as(byte[].class)` if any values are larger than 127. +The Graal polyglot sdk provides `org.graalvm.polyglot.io.ByteSequence` as a target type to deal with this issue explicitly. + +```java +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.io.ByteSequence; +Value result = context.eval("python", "b'hello'"); +ByteSequence seq = result.as(ByteSequence.class); // does not copy +``` + +`ByteSequence` keeps the data as a Python-owned byte sequence without immediately copying. +It provides a `toByteArray()` method that deals with re-interpreting unsigned Python bytes as signed Java bytes. + +```java +import java.nio.charset.StandardCharsets; +import org.graalvm.polyglot.io.ByteSequence; +ByteSequence seq = result.as(ByteSequence.class); +byte[] bytes = seq.toByteArray(); // copies into Java byte[] +String s = new String(bytes, StandardCharsets.UTF_8); +``` + +## Call Other Languages from Python The _polyglot_ API allows non-JVM specific interactions with other languages from Python scripts. This includes all interactions with dynamic languages supported via the [Truffle framework](https://www.graalvm.org/latest/graalvm-as-a-platform/language-implementation-framework/), including JavaScript and Ruby. -## Multi-threading and other languages +## Multithreading GraalPy implements the Python global interpreter lock (GIL), which prevents any two threads from executing Python code at the same instant. When methods in other languages are called from Python, no Python code is running while the other language executes. @@ -153,7 +211,7 @@ If you're using Maven with GraalPy, add the JavaScript dependency to your _pom.x org.graalvm.polyglot js - 25.0.2 + 25.0.3 ``` @@ -225,7 +283,7 @@ Here are practical examples of using the `polyglot` API to work with JavaScript This program matches Python strings using the JavaScript regular expression object. Python reads the captured group from the JavaScript result and checks for a substring in it. -## Exporting Python Objects to other languages +## Export Python Objects Use the `polyglot` module to expose Python objects to JVM languages and other Graal languages (languages implemented on the [Truffle framework](https://www.graalvm.org/latest/graalvm-as-a-platform/language-implementation-framework/)). @@ -236,7 +294,7 @@ This allows other languages to call your Python code directly. import ssl polyglot.export_value(value=ssl, name="python_ssl") ``` - + Then use it, for example, from JavaScript code: ```js Polyglot.import('python_ssl').get_server_certificate(["oracle.com", 443]) @@ -253,7 +311,7 @@ This allows other languages to call your Python code directly. ```java import org.graalvm.polyglot.*; import org.graalvm.python.embedding.GraalPyResources; - + class Main { public static void main(String[] args) { try (var context = GraalPyResources.createContext()) { @@ -270,7 +328,7 @@ This allows other languages to call your Python code directly. } ``` -## Mapping types between Python and other languages +## Types Mapping The interop protocol defines different types and traits that determine foreign objects behavior and restrictions when used in Python. @@ -282,7 +340,7 @@ Types not listed in the table below have no special interpretation in Python. | Interop Type | Inherits from | Python Interpretation | | :------------- | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------ | -| `array` | ForeignList, `list` | An `array` behaves like a Python `list`. | +| `array` | ForeignList, `list` | An `array` behaves like a Python `list`. Just like Python lists, the size must fit into a 32-bit signed integer. | | `boolean` | ForeignBoolean, ForeignNumber | `boolean` behaves like Python booleans, including the fact that in Python, all booleans are also integers (`1` and `0` for `true` and `false`, respectively). | | `buffer` | ForeignObject | Buffers work like Python buffer objects (such as those used with `memoryview`) to avoid copying data. | | `exception` | ForeignException, `BaseException` | An `exception` can be caught in a generic `except` clause. | @@ -325,7 +383,7 @@ The following table shows how Python objects are converted to interop types when | `number` | Only subtypes of `int` and `float`. | | `string` | Only subtypes of `str`. | -## The Interoperability Extension API +## Interoperability Extension API You can extend the interoperability protocol directly from Python through a simple API defined in the `polyglot` module. This API lets you define interoperability behavior for custom or user-defined types that are not automatically supported. @@ -519,7 +577,8 @@ class MyPythonClass: ### Supported messages -Most interop messages are supported by the interop behavior extension API. The naming convention for `register_interop_behavior` keyword arguments uses _snake_case_, so the interop `fitsInLong` message becomes `fits_in_long`. +Most interop messages are supported by the interop behavior extension API. +The naming convention for `register_interop_behavior` keyword arguments uses _snake_case_, so the interop `fitsInLong` message becomes `fits_in_long`. Each message can be extended with either a **pure Python function** (no default keyword arguments, free vars, or cell vars allowed) or a **boolean constant**. The following table describes the supported interop messages: @@ -580,4 +639,4 @@ The following table describes the supported interop messages: | `isHashEntryRemovable` | `is_hash_entry_removable` | `bool` | | `readHashValue` | `read_hash_value` | `object` | | `writeHashEntry` | `write_hash_entry` | `None` | -| `removeHashEntry` | `remove_hash_entry` | `None` | +| `removeHashEntry` | `remove_hash_entry` | `None` | diff --git a/docs/user/Native-Extensions.md b/docs/user/Native-Extensions.md index ecbeb12830..094042216f 100644 --- a/docs/user/Native-Extensions.md +++ b/docs/user/Native-Extensions.md @@ -7,49 +7,3 @@ Packages that use the native API must be built and installed with GraalPy, and t For best results, it is crucial that you only use the `pip` command that comes preinstalled in GraalPy virtual environments to install packages. The version of `pip` shipped with GraalPy applies additional patches to packages upon installation to fix known compatibility issues and it is preconfigured to use an additional repository from graalvm.org where we publish a selection of prebuilt wheels for GraalPy. Please do not update `pip` or use alternative tools such as `uv`. - -## Embedding limitations - -Python native extensions run by default as native binaries, with full access to the underlying system. -This has a few implications: - -1. Native code is entirely unrestricted and can circumvent any security protections Truffle or the JVM may provide. -2. Native data structures are not subject to the Java GC and the combination of them with Java data structures may lead to increased memory pressure or memory leaks. -3. Native libraries generally cannot be loaded multiple times into the same process, and they may contain global state that cannot be safely reset. - -### Full Native Access - -The Context API allows to set options such as `allowIO`, `allowHostAccess`, `allowThreads` and more on the created contexts. -To use Python native extensions on GraalPy, the `allowNativeAccess` option must be set to true, but this opens the door to full native access. -This means that while Python code may be denied access to the host file system, thread- or subprocess creation, and more, the native extension is under no such restriction. - -### Memory Management - -Python C extensions, like the CPython reference implementation, use reference counting for memory management. -This is fundamentally incompatible with JVM GCs. - -Java objects may end up being referenced from native data structures which the JVM cannot trace, so to avoid crashing, GraalPy keeps such Java objects strongly referenced. -To avoid memory leaks, GraalPy implements a cycle detector that regularly traces references between Java objects and native objects that have crossed between the two worlds and cleans up strong references that are no longer needed. - -On the other side, reference-counted native extension objects may end up being referenced from Java objects, and in this case GraalPy bumps their reference count to make them unreclaimable. -Any such references to native extension objects are registered with a `java.lang.ref.WeakReference` and when the JVM GC has collected the owning Java object, the reference count of the native object is reduced again. - -Both of these mechanisms together mean there is additional delay between objects becoming unreachable and their memory being reclaimed when compared to the CPython implementation. -This can manifest in increased memory usage when running C extensions. -You can tweak the Context options `python.BackgroundGCTaskInterval`, `python.BackgroundGCTaskThreshold`, and `BackgroundGCTaskMinimum` to mitigate this. -They control the minimum interval between cycle detections, how much RSS memory must have increased since the last time to trigger the cycle detector, and the absolute minimum RSS under which no cycle detection should be done. -You can also manually trigger the detector with the Python `gc.collect()` call. - -### Multi-Context and Native Libraries - -Using C extensions in multiple contexts is only possible on Linux for now, and many C extensions still have issues in this mode. -You should test your applications thoroughly if you want to use this feature. -There are many possibilities for native code to sidestep the library isolation through other process-wide global state, corrupting the state and leading to incorrect results or crashing. -The implementation also relies on `venv` to work, even if you are not using external packages. - -To support creating multiple GraalPy contexts that access native modules within the same JVM or Native Image, we need to isolate them from each other. -The current strategy for this is to copy the libraries and modify them such that the dynamic library loader of the operating system will isolate them for us. -To do this, all GraalPy contexts in the same process (not just those in the same engine!) must set the `python.IsolateNativeModules` option to `true`. -You should test your applications thoroughly if you want to use this feature, as there are many possiblities for native code to sidestep the library isolation through other process-wide global state. - -For more details on this, see [our implementation details](https://github.com/oracle/graalpython/blob/master/docs/contributor/IMPLEMENTATION_DETAILS.md#c-extension-copying). diff --git a/docs/user/Native-Images-with-Python.md b/docs/user/Native-Images-with-Python.md index 3ca11470ea..2abd76988c 100644 --- a/docs/user/Native-Images-with-Python.md +++ b/docs/user/Native-Images-with-Python.md @@ -2,9 +2,9 @@ GraalPy supports GraalVM Native Image to generate native binaries of Java applications that embed Python code. -## Quickstart +## Building Executables with Python -If you started with the [Maven archetype](README.md), the generated _pom.xml_ file already includes the necessary configuration for creating a native executable using the [Maven plugin for Native Image building](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html). +If you started with the [Maven archetype](Embedding-Getting-Started.md#maven-quick-start), the generated _pom.xml_ file already includes the necessary configuration for creating a native executable using the [Maven plugin for Native Image building](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html). To build the application, run: @@ -30,6 +30,7 @@ You can significantly reduce the size by excluding components your application d By default, GraalPy includes a pre-initialized Python context in the executable for faster startup. Disabling this reduces the binary size by about 15MiB. You should remove this if: + - You are creating more than one context - Binary size is more important than a slight startup delay @@ -65,4 +66,4 @@ Since every application is different, experiment with different combinations to ## Shipping Python Packages -Our Maven archetype by default is set up to include all needed Python files in the native binary itself, so the image is self-contained. +GraalPy Maven archetype by default is set up to include all needed Python files in the native binary itself, so the image is self-contained. diff --git a/docs/user/Performance.md b/docs/user/Performance.md index fa84d2be43..7d9c1e0dc3 100644 --- a/docs/user/Performance.md +++ b/docs/user/Performance.md @@ -17,6 +17,16 @@ Many Python packages from the machine learning or data science ecosystems contai This code benefits little from GraalPy's JIT compilation and suffers from having to emulate CPython implementation details on GraalPy. When many C extensions are involved, performance can vary a lot depending on the specific interactions of native and Python code. +### Launcher JIT Presets + +The GraalPy launcher provides `-X jit=0|1|2` presets for common startup-heavy and throughput-oriented use cases. The launcher defaults to `-X jit=1` when no `-X jit=...` preset is specified: + +- `-X jit=0` disables runtime compilation and minimizes memory usage for tiny one-shot runs. +- `-X jit=1` keeps compilation enabled, but favors small command line applications with a single compiler thread and higher compilation thresholds. +- `-X jit=2` keeps compilation enabled with throughput mode and the same higher thresholds for hotter or longer-running workloads. + +These presets are launcher conveniences that expand to engine options. They can still be combined with explicit `--engine.*` options when more detailed tuning is needed. + ## Code Loading Performance and Footprint It takes time to parse Python code so when using GraalPy to embed another language in Python, observe the general advice for embedding Graal languages related to [code caching](https://www.graalvm.org/latest/reference-manual/embed-languages/#code-caching-across-multiple-contexts). diff --git a/docs/user/Platform-Support.md b/docs/user/Platform-Support.md new file mode 100644 index 0000000000..bf8f1ef1a5 --- /dev/null +++ b/docs/user/Platform-Support.md @@ -0,0 +1,7 @@ +### Platform Support + +GraalPy is mostly written in Java and Python, but the Python package ecosystem is rich in native packages that need platform specific support via native libraries that expose platform-specific APIs. +The main operating system is Oracle Linux, the CPU architectures are AMD64 and ARM, and the primary JDK is Oracle GraalVM. +**Linux is recommended for getting started with GraalPy.** Windows and macOS with GraalVM JDK are less well tested, and outside of those combinations only basic test coverage is provided. +As macOS and other platforms are not prioritized, some GraalPy features may not work on these platforms. +See [Test Tiers](../user/Test-Tiers.md) for a detailed breakdown. \ No newline at end of file diff --git a/docs/user/Python-Runtime.md b/docs/user/Python-Runtime.md index 59e5f10e19..b8730d9354 100644 --- a/docs/user/Python-Runtime.md +++ b/docs/user/Python-Runtime.md @@ -1,159 +1,19 @@ -# Testing Python Applications and Packages on GraalPy +# GraalPy Runtime Guide -## Choosing the GraalPy Runtime +This content has been merged into [Using GraalPy as a Standalone Python Runtime](Standalone-Getting-Started.md). -GraalPy provides a Python 3.12 compliant runtime. -A primary goal is to support PyTorch, SciPy, and their constituent libraries, as well as to work with other data science and machine learning libraries from the rich Python ecosystem. -GraalPy is distributed as an ahead-of-time compiled native executable, compact in size. +## Choosing a GraalPy Distribution -GraalPy provides the following capabilities: +See [Choosing a GraalPy Distribution](Standalone-Getting-Started.md#choosing-a-graalpy-distribution). -* CPython-compatible distribution. This is the most compatible option to test Python code on GraalPy, since it most closely resembles the structure of CPython distributions. -* Unique deployment mode for Python applications. Compile a Python application on GraalPy to [a single native binary](Python-Standalone-Applications.md) that embeds all needed resources. -* Access to GraalVM's language ecosystems and tools. GraalPy can run many standard Python tools as well as tools from the GraalVM ecosystem. +## GraalPy Capabilities -### GraalPy Distributions - -GraalPy is available as **GraalPy built on Oracle GraalVM** and **GraalPy Community**. - -* GraalPy built on top of Oracle GraalVM provides the best experience: it comes with additional optimizations, is significantly faster and more memory-efficient. It is licensed under the [GraalVM Free Terms and Conditions (GFTC)](https://www.oracle.com/downloads/licenses/graal-free-license.html) license, same as Oracle GraalVM, which permits use by any user including commercial and production use. -Redistribution is permitted as long as it is not for a fee. - -* GraalPy Community is built on top of GraalVM Community Edition, and is fully open-source. - -Two language runtime options are available for the Oracle and Community distributions of GraalPy: - -* Native - * GraalPy is compiled ahead-of-time to a native executable. - * This means that you do not need a JVM to run GraalPy and it is compact in size. -* JVM - * You can easily exploit Java interoperability. - * Peak performance may be higher than the native option. - -### GraalPy Identification - -The four GraalPy runtimes are identified as follows, using the general pattern _graalpy(-community)(-jvm)-<version>-<os>-<arch>_: - - - - - - - - - - - - - - - - - -
 OracleCommunity
Nativegraalpy-<version>-<os>-<arch>graalpy-community-<version>-<os>-<arch>
JVMgraalpy-jvm-<version>-<os>-<arch>graalpy-community-jvm-<version>-<os>-<arch>
- - -### Comparison - -| Runtime | Native (default) | JVM | -|:-------|:-----------------|:----| -|Time to start | faster | slower | -| Time to reach peak performance | faster | slower | -| Peak performance (also considering GC) |good | best | -| Java interoperability | needs configuration | works | +See [GraalPy Capabilities](Standalone-Getting-Started.md#graalpy-capabilities). ## Installing GraalPy -> NOTE: There will be a delay between GraalPy release and its availability on Pyenv. - -### Linux - -The easiest way to install GraalPy on Linux is to use [Pyenv](https://github.com/pyenv/pyenv) (the Python version manager). -To install version 25.0.2 using Pyenv, run the following commands: -```bash -pyenv install graalpy-25.0.2 -``` -```bash -pyenv shell graalpy-25.0.2 -``` -> Before running `pyenv install`, you may need to update `pyenv` to include the latest GraalPy versions. - -Alternatively, you can download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases). - -1. Find the download that matches the pattern _graalpy-XX.Y.Z-linux-amd64.tar.gz_ or _graalpy-XX.Y.Z-linux-aarch64.tar.gz_ (depending on your platform) and download. -2. Uncompress the file and update your `PATH` environment variable to include to the _graalpy-XX.Y.Z-linux-amd64/bin_ (or _graalpy-XX.Y.Z-linux-aarch64/bin_) directory. - -### macOS - -The easiest way to install GraalPy on macOS is to use [Pyenv](https://github.com/pyenv/pyenv) (the Python version manager). -To install version 25.0.2 using Pyenv, run the following commands: -```bash -pyenv install graalpy-25.0.2 -``` -```bash -pyenv shell graalpy-25.0.2 -``` -> Before running `pyenv install`, you may need to update `pyenv` to include the latest GraalPy versions. - -Alternatively, you can download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases). - -1. Find the download that matches the pattern _graalpy-XX.Y.Z-macos-aarch64.tar.gz_ and download. -2. Remove the quarantine attribute. - ```bash - sudo xattr -r -d com.apple.quarantine /path/to/graalpy - ``` - For example: - ```bash - sudo xattr -r -d com.apple.quarantine ~/.pyenv/versions/graalpy-25.0.2 - ``` -3. Uncompress the file and update your `PATH` environment variable to include to the _graalpy-XX.Y.Z-macos-aarch64/bin_ directory. - -### Windows - -1. Find and download a compressed GraalPy installation file from [GitHub releases](https://github.com/oracle/graalpython/releases) that matches the pattern _graalpy-XX.Y.Z-windows-amd64.tar.gz_. -2. Uncompress the file and update your `PATH` variable to include to the _graalpy-XX.Y.Z-windows-amd64/bin_ directory. - -#### Windows Limitations - -The Windows distribution of GraalPy has more limitations than its Linux or macOS counterpart, so not all features and packages may be available. - -It has the following known issues: -- JLine treats Windows as a dumb terminal, with no autocomplete and limited editing capabilities in the REPL -- Interactive `help()` in the REPL doesn't work -- Inside a virtual environment: - - _graalpy.cmd_ and _graalpy.exe_ are broken - - _pip.exe_ cannot be used directly - - `pip` has trouble with cache file loading, use `--no-cache-dir` - - Only pure Python binary wheels can be installed, no native extensions or source builds - - To install a package, use `myvenv/Scripts/python.exe -m pip --no-cache-dir install ` -- Running from PowerShell works better than running from CMD, various scripts will fail on the latter +See [Installation](Standalone-Getting-Started.md#installation). ## Installing Packages -The best way of using GraalPy is from a [venv](https://docs.python.org/3/library/venv.html) virtual environment. -This generates wrapper scripts and makes the implementation usable from a shell as the standard Python interpreter. - -1. Create a virtual environment with GraalPy by running the following command: - ```bash - graalpy -m venv - ``` - For example: - ```bash - graalpy -m venv ~/.virtualenvs/graalpy-25.0.2 - ``` - -2. Activate the environment in your shell session: - ```bash - source /bin/activate - ``` - For example: - ```bash - source ~/.virtualenvs/graalpy-25.0.2/bin/activate - ``` - -Multiple executables are available in the virtual environment, including: `python`, `python3`, and `graalpy`. - -> Note: To deactivate the Python environment (and return to your shell), run `deactivate`. - -The `pip` package installer is available when using a virtual environment. -The GraalPy implementation of `pip` may choose package versions other than the latest in cases where it ships patches to make these work better. +See [Using Virtual Environments](Standalone-Getting-Started.md#using-virtual-environments). diff --git a/docs/user/Python-Standalone-Applications.md b/docs/user/Python-Standalone-Applications.md index eaa5c32a1d..3025ce734f 100644 --- a/docs/user/Python-Standalone-Applications.md +++ b/docs/user/Python-Standalone-Applications.md @@ -1,16 +1,19 @@ # Python Standalone Applications -GraalPy enables you to package your Python applications or libraries into native executables or JAR files with no external dependencies. This means users can run your application without installing Python or any packages. +GraalPy enables you to package your Python applications or libraries into native executables or JAR files with no external dependencies. +This means users can run your application without installing Python or any packages. -GraalPy uses the [Truffle framework](https://github.com/oracle/graal/tree/master/truffle) to bundle your Python code, dependencies, and the Python runtime into standalone executables. Truffle's filesystem virtualization allows everything to work from a single file, though packages with native C extensions may have limitations. +GraalPy uses the [Truffle framework](https://github.com/oracle/graal/tree/master/truffle) to bundle your Python code, dependencies, and the Python runtime into standalone executables. +Truffle's filesystem virtualization allows everything to work from a single file, though packages with native C extensions may have limitations. -GraalPy includes a module named `standalone` to create a Python binary for Linux, macOS, and Windows. The module bundles all your application's resources into a single file. +GraalPy includes a module named `standalone` to create a Python binary for Linux, macOS, and Windows. +The module bundles all your application's resources into a single file. > **Prerequisite:** GraalPy 23.1.0 or later. [Download here](https://github.com/oracle/graalpython/releases) or verify your version with `graalpy --version`. -## Quickstart of Python Standalone Applications +## Running Python Standalone Applications -To create an native executable from a Python file with its dependencies, use this command: +To create a native executable from a Python file with its dependencies, use this command: ```bash graalpy -m standalone native \ @@ -30,4 +33,5 @@ Use `graalpy -m standalone native --help` for further options. ### Security Considerations of Python Standalone Applications -Standalone executables do not protect your source code. Your Python code becomes bytecode, and bytecode can be easily decompiled back to readable Python code. +Standalone executables do not protect your source code. +Your Python code becomes bytecode, and bytecode can be easily decompiled back to readable Python code. diff --git a/docs/user/Python-on-JVM.md b/docs/user/Python-on-JVM.md index 5aa929c537..374e9cdad4 100644 --- a/docs/user/Python-on-JVM.md +++ b/docs/user/Python-on-JVM.md @@ -1,28 +1,40 @@ -# Modern Python for the JVM +# Migrate from Jython to GraalPy -For Python version 2 (now EOL), Jython is the _de facto_ means of interfacing Python and Java. -Most existing Jython code that uses Java integration will be based on a stable Jython release—however, these are only available in Python 2.x versions. -GraalPy, in contrast, is compatible with Python 3.x and does not provide full compatibility with earlier 2.x versions of Jython. +Jython has been the standard way to run Python code on the JVM and integrate with Java libraries. +However, Jython's latest stable releases only support Python 2.x, which reached end-of-life (EOL) in 2020. -To migrate code from Python 2 to Python 3, follow [the official guide from the Python community](https://docs.python.org/3/howto/pyporting.html). -Once your Jython code is compatible with Python 3, follow this guide to iron out other differences between GraalPy and Jython. +GraalPy provides a modern alternative that supports Python 3.x on the JVM with excellent Java interoperability. +While GraalPy offers similar capabilities to Jython, there are important differences in how Java integration works. -GraalPy's first-class support for [Java interoperability](Interoperability.md) makes using Java libraries from Python as easy as possible, with special affordances for Java code beyond the generic interoperability support for other Graal languages (languages implemented on the [Truffle framework](https://www.graalvm.org/latest/graalvm-as-a-platform/language-implementation-framework/)). +This guide shows you how to migrate from Jython to GraalPy. -Not all features of Jython are supported on GraalPy. -Some are supported, but disabled by default due to their negative impact on runtime performance. -During migration, you can enable these features using a command line option: `--python.EmulateJython`. -We recommend to move away from these features, however, to achieve optimal performance. +For a lightweight command-line JVM launcher similar to the Jython launcher, use the JBang recipe from the GraalPy catalog: -## Migrating Jython Scripts +```bash +jbang graalpy@oracle/graalpython -c "print('hello from GraalPy')" +``` + +### Prerequisites + +- To migrate plain Jython command-line scripts, you can create a custom embedding or run GraalPy with the JBang recipe. +- To migrate applications that embed Jython, create a GraalPy embedding. For more information, see [Embedding Python in Java](Embedding-Getting-Started.md). +- Make sure to migrate code from Python 2 (as supported by Jython) to Python 3 (the major version GraalPy is compatible with) following [the official guide from the Python community](https://docs.python.org/3/howto/pyporting.html) + +## GraalPy Java Interoperability Overview + +GraalPy provides excellent [Java interoperability](Interoperability.md), allowing you to use Java libraries from Python with minimal friction. +It offers specialized features for Java integration that go beyond what's available for other languages on the GraalVM platform. + +**Important:** GraalPy doesn't support all Jython features out of the box. +Some features are available but disabled by default for performance reasons. +You can enable them during migration with `--python.EmulateJython`, but updating your code to use GraalPy's native approach is recommended for better performance. -> To move plain Jython scripts from Jython to GraalPy, use a GraalPy JVM-based runtime. -> (For more information, see available [GraalPy Distributions](Python-Runtime.md)). +## Java Package Imports -### Importing a Java Package +### Packages That Work by Default -There are certain features of Jython's Java integration that are enabled by default on GraalPy. -Here is an example: +Certain features of Jython's Java integration are enabled by default on GraalPy. +Here's what works the same: ```python >>> import java.awt as awt @@ -36,10 +48,12 @@ Here is an example: This example produces the same result when run on both Jython and GraalPy. However, when the example is run on GraalPy, only packages that are in the `java` namespace can be imported directly. -To import classes from packages outside the `java` namespace, use the `--python.EmulateJython` option. -> Note: When embedding GraalPy in a modularized application, you may have to -> add exports for the required modules according to JSR 376. +### Packages Outside the Java Namespace + +To import classes from packages outside the `java` namespace, use the `--python.EmulateJython` option during migration. + +> **Note:** When embedding GraalPy in a modularized application, you may have to add exports for the required modules according to JSR 376. Additionally, it is not possible to import Java packages as Python modules in all circumstances. For example, this will work: @@ -55,17 +69,18 @@ import javax.swing as swing from javax.swing import * ``` -Instead, import one of the classes directly: +Instead, import classes directly: ```python import javax.swing.Window as Window ``` -### Basic Object Usage +## Java Object Usage + +### Working with Java Objects -Constructing and working with Java objects and classes is achieved with conventional -Python syntax. -The methods of a Java object can also be retrieved and referenced as first class objects (bound to their instance), in the same way as Python methods. For example: +Constructing and working with Java objects and classes is achieved with conventional Python syntax. +The methods of a Java object can also be retrieved and referenced as first class objects (bound to their instance), in the same way as Python methods: ```python >>> from java.util import Random @@ -77,13 +92,15 @@ The methods of a Java object can also be retrieved and referenced as first class 1672896916 ``` -### Java-to-Python Types: Automatic Conversion +### Type Conversion + +GraalPy automatically converts between Python and Java types to make method calls and data passing seamless. +When you call Java methods, GraalPy matches your Python arguments to the available Java parameter types using a best-effort approach. + +GraalPy's type matching is more flexible than Jython's as it can convert any Python object with `__int__` or `__float__` methods to the corresponding Java types. +This means you can use NumPy arrays as Java `int[]` or Pandas DataFrames as Java `double[][]` when the data types are compatible. -Method overloads are resolved by matching the Python arguments in a best-effort manner to the available parameter types. -This approach is also taken when converting data. -The goal here is to make using Java from Python as smooth as possible. -The matching approach taken by GraalPy is similar to Jython, but GraalPy uses a more dynamic approach to matching—Python types emulating `int` or `float` are also converted to the appropriate Java types. -This enables you, for example, to use a Pandas frame as `double[][]` or NumPy array elements as `int[]` when the elements fit into those Java primitive types. +Here are the supported type conversions: | Java type | Python type | |:-------------------------------|:----------------------------------------------------------------------------------------| @@ -98,18 +115,19 @@ This enables you, for example, to use a Pandas frame as `double[][]` or NumPy ar | Java objects | Wrapped Java object of the appropriate type | | `java.lang.Object` | Any object | -### Special Jython Module: `jarray` +## Java Arrays + +### Special Jython Module: jarray GraalPy implements the `jarray` module (to create primitive Java arrays) for compatibility. -This module is always available, since we have not found its presence to have a negative impact. -For example: +This module is always available, since we have not found its presence to have a negative impact: ```python >>> import jarray >>> jarray.array([1,2,3], 'i') ``` -Note that its usage is equivalent to constructing the array type using the `java.type` function and then populating the array, as follows: +Note that its usage is equivalent to constructing the array type using the `java.type` function and then populating the array: ```python >>> import java @@ -133,10 +151,9 @@ However, implicitly, this may produce a copy of the array data, which can be dec [98, 97, 122] ``` -### Exceptions from Java +## Java Exceptions -You can catch Java exceptions as you would expect. -For example: +You can catch Java exceptions as you would expect: ```python >>> import java @@ -149,110 +166,116 @@ For example: 7 >= 0 ``` -### Java Collections +## Java Collections -* Java arrays and collections that implement the `java.util.Collection` interface can be accessed using the `[]` syntax. +### Collection Interface Features + +Java arrays and collections that implement the `java.util.Collection` interface can be accessed using the `[]` syntax. An empty collection is considered `false` in boolean conversions. -The length of a collection is exposed by the `len` built-in function. -For example: +The length of a collection is exposed by the `len` built-in function: - ```python - >>> from java.util import ArrayList - >>> l = ArrayList() - >>> l.add("foo") - True - >>> l.add("baz") - True - >>> l[0] - 'foo' - >>> l[1] = "bar" - >>> del l[1] - >>> len(l) - 1 - >>> bool(l) - True - >>> del l[0] - >>> bool(l) - False - ``` - -* Java iterables that implement the `java.lang.Iterable` interface can be iterated over using a `for` loop or the `iter` built-in function and are accepted by all built-ins that expect an iterable. -For example: +```python +>>> from java.util import ArrayList +>>> l = ArrayList() +>>> l.add("foo") +True +>>> l.add("baz") +True +>>> l[0] +'foo' +>>> l[1] = "bar" +>>> del l[1] +>>> len(l) +1 +>>> bool(l) +True +>>> del l[0] +>>> bool(l) +False +``` - ```python - >>> [x for x in l] - ['foo', 'bar'] - >>> i = iter(l) - >>> next(i) - 'foo' - >>> next(i) - 'bar' - >>> next(i) - Traceback (most recent call last): - File "", line 1, in - StopIteration - >>> set(l) - {'foo', 'bar'} - ``` - -* An iterator can be iterated as well. For example: - - ```python - >>> from java.util import ArrayList - >>> l = ArrayList() - >>> l.add("foo") - True - >>> i = l.iterator() # Calls the Java iterator methods - >>> next(i) - 'foo' - ``` - -* Mapped collections that implement the `java.util.Map` interface can be accessed using the `[]` notation. -An empty map is considered `false` in boolean conversions. Iteration of a map yields its keys, consistent with `dict`. -For example: +### Iterating Over Collections + +Java iterables that implement the `java.lang.Iterable` interface can be iterated over using a `for` loop or the `iter` built-in function and are accepted by all built-ins that expect an iterable: + +```python +>>> [x for x in l] +['foo', 'bar'] +>>> i = iter(l) +>>> next(i) +'foo' +>>> next(i) +'bar' +>>> next(i) +Traceback (most recent call last): +File "", line 1, in +StopIteration +>>> set(l) +{'foo', 'bar'} +``` + +You can also iterate over Java iterators directly: + +```python +>>> from java.util import ArrayList +>>> l = ArrayList() +>>> l.add("foo") +True +>>> i = l.iterator() # Calls the Java iterator methods +>>> next(i) +'foo' +``` + +### Working with Maps + +Mapped collections that implement the `java.util.Map` interface can be accessed using the `[]` notation. +An empty map is considered `false` in boolean conversions. +Iteration of a map yields its keys, consistent with `dict`: + +```python +>>> from java.util import HashMap +>>> m = HashMap() +>>> m['foo'] = 5 +>>> m['foo'] +5 +>>> m['bar'] +Traceback (most recent call last): +File "", line 1, in +KeyError: bar +>>> [k for k in m] +['foo'] +>>> bool(m) +True +>>> del m['foo'] +>>> bool(m) +False +``` - ```python - >>> from java.util import HashMap - >>> m = HashMap() - >>> m['foo'] = 5 - >>> m['foo'] - 5 - >>> m['bar'] - Traceback (most recent call last): - File "", line 1, in - KeyError: bar - >>> [k for k in m] - ['foo'] - >>> bool(m) - True - >>> del m['foo'] - >>> bool(m) - False - ``` - -### Inheritance from Java +## Inheritance from Java Classes + +### Understanding Java Class Inheritance Inheriting from a Java class (or implementing a Java interface) is supported with some syntactical and significant behavioral differences from Jython. To create a class that inherits from a Java class (or implements a Java interface), use the conventional Python `class` statement. Declared methods override (implement) superclass (interface) methods when their names match. -It is important to understand that there is actually delegation happening here - when inheriting from Java, two classes are created, one in Java and one in Python. +**Important:** There is actually delegation happening here - when inheriting from Java, two classes are created, one in Java and one in Python. These reference each other and any methods that are declared in Python that override or implement a Java method on the superclass are declared on the Java side as delegating to Python. The created object does not behave like a Python object but instead in the same way as a foreign Java object. The reason for this is that when you create an instance of your new class, you get a reference to the *Java* object. -#### Inheritance behavior up to GraalPy version 25.1 +### Inheritance behavior up to GraalPy version 25.1 When inheriting from a Java class, you can pass the keyword `new_style=False`. This is the default up to and including GraalPy version 25.1. -The generated class is a Java object and no forwarding to the Python-side happens by default. -To call Python methods that do *not* override or implement methods that already existed on the superclass, you need to use the special `this` attribute. -Once you are in a Python method, your `self` refers to the Python object, and to get back from a Python method to Java, use the special attribute `__super__`. -And since we do not expose static members on the instance side, if you need to call a static method from an instance on the Java side, use `getClass().static` to get to the meta-object holding the static members. -One important consequence of the two-object-schema here is that the `__init__` method on the Python object is actually called *before* the connection to the Java side is established. -So you cannot currently override construction of the Java object or run code during initialization that would affect the Java half of the combined structure. -You will have to create a factory method if you want to achieve this. +Key points for legacy inheritance: + +- The generated class is a Java object and no forwarding to the Python-side happens by default +- To call Python methods that do *not* override or implement methods that already existed on the superclass, you need to use the special `this` attribute +- Once you are in a Python method, your `self` refers to the Python object, and to get back from a Python method to Java, use the special attribute `__super__` +- If you need to call a static method from an instance on the Java side, use `getClass().static` to get to the meta-object holding the static members +- The `__init__` method on the Python object is actually called *before* the connection to the Java side is established, so you cannot currently override construction of the Java object For example: @@ -260,7 +283,6 @@ For example: import atexit from java.util.logging import Logger, Handler - class MyHandler(Handler): def __init__(self): self.logged = [] @@ -268,7 +290,6 @@ class MyHandler(Handler): def publish(self, record): self.logged.append(record) - logger = Logger.getLogger("mylog") logger.setUseParentHandlers(False) handler = MyHandler() @@ -286,15 +307,17 @@ for record in handler.this.logged: For more information about how the generated Java subclass behaves, see the [Truffle documentation](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleLanguage.Env.html#createHostAdapter(java.lang.Object%5B%5D)). -#### Inheritance behavior from GraalPy 25.1 +### Inheritance behavior from GraalPy 25.1 -When inheriting from a Java class, you can pass the keyword `new_style=True`. -This is the default after GraalPy version 25.1. +When inheriting from a Java class, you can pass the keyword `new_style=True`. This is the default after GraalPy version 25.1. The generated class is a Java object, but attribute lookup is dispatched to the Python object as needed. -Multiple levels of inheritance are supported, and `super()` calls both in the constructor override via `__new__` as well as in Java method overrides work as expected. -The `self` in a method refers to the Java object, but any access that does not refer to a field or method on the Java class is transparently dispatched to the Python side. -Static methods can be called both from the instance as well as the class. +Features of modern inheritance: + +- Multiple levels of inheritance are supported +- `super()` calls work both in the constructor override via `__new__` as well as in Java method overrides +- The `self` in a method refers to the Java object, but any access that does not refer to a field or method on the Java class is transparently dispatched to the Python side +- Static methods can be called both from the instance as well as the class For example: @@ -327,25 +350,23 @@ class PythonLevel(Level, new_style=True): instance as well as the class""" return self.parse(name) - pl = PythonLevel() assert issubclass(PythonLevel, Level) assert PythonLevel.parse("INFO").getName() == "INFO" ``` - ## Embedding Python into Java -The other way to use Jython was to embed it into a Java application. There were two options for such an embedding. +If you were embedding Jython in Java applications, there were two main approaches that need different migration paths: + +- **`PythonInterpreter` approach:** Use the `PythonInterpreter` object that Jython provides. + + Existing code using Jython in this manner depends directly on the Jython package (for example, in the Maven configuration), because the Java code has references to Jython internal classes. These classes do not exist in GraalVM, and no equivalent classes are exposed. + + Switch to the [GraalVM SDK](https://central.sonatype.com/artifact/org.graalvm.sdk/graal-sdk). Using this SDK, no APIs particular to Python are exposed, everything is achieved via the GraalVM API, with maximum configurability of the Python runtime. Refer to the [Embedding Getting Started](Embedding-Getting-Started.md) documentation for preparing a setup. + +- **JSR 223 `ScriptEngine` approach:** Embed Jython in Java via [JSR 223](https://www.jcp.org/en/jsr/detail?id=223) by using the classes of the `javax.script` package, and, in particular, via the `ScriptEngine` class. -1. Use the `PythonInterpreter` object that Jython provides. -Existing code using Jython in this manner depends directly on the Jython package (for example, in the Maven configuration), because the Java code has references to Jython internal classes. -These classes do not exist in GraalVM, and no equivalent classes are exposed. -To migrate from this usage, switch to the [GraalVM SDK](https://central.sonatype.com/artifact/org.graalvm.sdk/graal-sdk). -Using this SDK, no APIs particular to Python are exposed, everything is achieved via the GraalVM API, with maximum configurability of the Python runtime. -Refer to the [Getting Started](README.md) documentation for preparing a setup. + This approach is not recommended, because the `ScriptEngine` APIs are not a clean fit for the options and capabilities of GraalPy. -2. Embed Jython in Java via [JSR 223](https://www.jcp.org/en/jsr/detail?id=223) by using the classes of the the `javax.script` package, and, in particular, via the `ScriptEngine` class. -We do not recommend this approach, because the `ScriptEngine` APIs are not a clean fit for the options and capabilities of GraalPy. -However, to migrate existing code, we provide an example ScriptEngine implementation that you can inline into your project. -Refer to [the reference manual for embedding](https://www.graalvm.org/latest/reference-manual/embed-languages/#compatibility-with-jsr-223-scriptengine) for details. + However, to migrate existing code, an example ScriptEngine implementation is provided that you can inline into your project. Refer to [the Embedding Languages reference manual](https://www.graalvm.org/latest/reference-manual/embed-languages/#compatibility-with-jsr-223-scriptengine) for details. diff --git a/docs/user/README.md b/docs/user/README.md index 591ab5c31d..f28d486032 100644 --- a/docs/user/README.md +++ b/docs/user/README.md @@ -1,8 +1,50 @@ -# Getting Started with GraalPy on the JVM +# GraalPy User Guide -You can use GraalPy with GraalVM JDK, Oracle JDK, or OpenJDK. -To add GraalPy to your Java application, use Maven or Gradle as shown below. -For other build systems (like Ant, Make, CMake, etc.), manual configuration may be required. +GraalPy is a Python 3.12 compliant runtime that provides better performance, native compilation capabilities, and seamless Java interoperability. + +Here are the two main types of users and how they can benefit from GraalPy: + +**For JVM developers** who need Python libraries in their applications or have legacy Jython code, GraalPy can be embedded directly into JVM projects using Maven or Gradle, see the [Embed Python in Java](#embedding-python-in-java) section. + +**For Python developers** who want better performance and native compilation, GraalPy serves as a drop-in replacement for standard Python, see the [GraalPy as CPython Alternative](#using-graalpy-as-a-standalone-python-runtime) section. + +## Embedding Python in Java + +**For JVM developers who need to use Python libraries from their JVM applications or migrate from legacy Jython code.** + +You do not need to install GraalPy separately - you can use GraalPy directly in Java with Maven or Gradle. +This lets you call Python libraries like NumPy, pandas, or any PyPI package from your Java application. +GraalPy also provides a migration path from Jython 2.x to Python 3.x with better performance and maintained Java integration capabilities. + +These guides cover everything you need to know: + +- **[Getting Started](Embedding-Getting-Started.md)** - Maven and Gradle setup +- **[Build Tools](Embedding-Build-Tools.md)** - Detailed plugin documentation +- **[Permissions](Embedding-Permissions.md)** - Configure security settings +- **[Interoperability](Interoperability.md)** - Java and Python integration patterns +- **[Native Images](Native-Images-with-Python.md)** - Compile to native executables +- **[Migration Guide](Python-on-JVM.md)** - Complete Jython to GraalPy migration + +## Using GraalPy as a Standalone Python Runtime + +**You want to use GraalPy instead of the standard Python from python.org.** + +Install GraalPy on your machine and use it like any Python interpreter. +You get better performance, the ability to compile to native binaries, and access to the GraalVM ecosystem. + +These guides cover everything you need to know: + +- **[Getting Started](Standalone-Getting-Started.md)** - Installation and basic usage +- **[Standalone Applications](Python-Standalone-Applications.md)** - Compile Python to native binaries +- **[Native Extensions](Native-Extensions.md)** - Working with C extensions and native packages +- **[Interoperability](Interoperability.md)** - Use Java and other Graal languages from Python +- **[Performance](Performance.md)** - Optimization tips and benchmarks +- **[Tooling](Tooling.md)** - IDE integration and development tools + +## General Information + +- **[Test Tiers](Test-Tiers.md)** - Platform compatibility and testing information +- **[Troubleshooting](Troubleshooting.md)** - Common embedding issues and solutions ## Version Compatibility @@ -14,211 +56,10 @@ The following table shows which Python versions are supported by each GraalPy re | 23.x | Python 3.10.8 | Oracle GraalVM for JDK 21.x, Oracle GraalVM for JDK 17.x | | 22.x | Python 3.8.5 | GraalVM Enterprise Edition 21.3.x | -### Platform support +### Platform Support GraalPy is mostly written in Java and Python, but the Python package ecosystem is rich in native packages that need platform specific support via native libraries that expose platform-specific APIs. The main operating system is Oracle Linux, the CPU architectures are AMD64 and ARM, and the primary JDK is Oracle GraalVM. -**Linux is recommended for getting started with GraalPy.** Windows and macOS with GraalVM JDK are less well tested, and outside of those combinations only basic test coverage is provided. As macOS and other platforms are not prioritized, some GraalPy features may not work on these platforms. -See [below](Test-Tiers.md) for a detailed breakdown. - -## Maven - -GraalPy can generate a Maven project that embeds Python packages into a Java application using [Maven artefacts](https://central.sonatype.com/namespace/org.graalvm.python). - -1. GraalPy project publishes a Maven archetype to generate a starter project: - ```bash - mvn archetype:generate \ - -DarchetypeGroupId=org.graalvm.python \ - -DarchetypeArtifactId=graalpy-archetype-polyglot-app \ - -DarchetypeVersion=25.0.2 - ``` - -2. Build a native executable using the [GraalVM Native Image "tool"](https://www.graalvm.org/latest/reference-manual/native-image/) plugin that was added for you automatically: - ```bash - mvn -Pnative package - ``` - -3. Once completed, run the executable: - ```bash - ./target/polyglot_app - ``` - The application prints "hello java" to the console. - -The project uses the [GraalVM Polyglot API](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/package-summary.html) with additional features to manage Python virtual environments and integrate Python package dependencies with a Maven workflow. -The Java code and the _pom.xml_ file are heavily documented and the generated code describes available features. - -See also [Embedding Build Tools](Embedding-Build-Tools.md#graalpy-maven-plugin) for more information about the GraalPy Maven Plugin. - -### Creating Cross-platform JARs with Native Python Packages - -The generated project uses the GraalPy Maven plugin, which makes it easy to add Python dependencies. -However, Python packages may have native components that are specific to the build system. -In order to distribute the resulting application for other systems, follow these steps: - -1. Build the project on each deployment platform. - Rename JAR files so they each have a platform-specific name and move them to a temporary directory on the same machine. - -2. Unzip each of the JAR files (substituting the correct names for the JAR files). - A special file, _vfs/fileslist.txt_ needs to be concatenated from each JAR file. - Finally, create a new _combined.jar_ from the combination of all files and with the concatenated _fileslist.txt_. - ```bash - unzip linux.jar -d combined - mv combined/vfs/fileslist.txt fileslist-linux.txt - unzip windows.jar -d combined - mv combined/vfs/fileslist.txt fileslist-windows.txt - cat fileslist-linux.txt fileslist-windows.txt > combined/vfs/fileslist.txt - cd combined - zip -r ../combined.jar * - ``` - -## Gradle - -1. Create a Java application with Gradle using the command below and follow the prompts (select the Groovy build script language, select a test framework, and so on): - ```bash - gradle init --type java-application \ - --project-name interop \ - --package interop \ - --no-split-project - ``` - - The project is generated in the current working directory with the following structure: - ```bash - └── app - ├── build.gradle - └── src - └── main - ├── java - │ └── interop - │ └── App.java - └── resources - ``` - -2. Open your project configuration file, _app/build.gradle_, and modify it as follows. - - Include the GraalPy support and the [GraalVM Polyglot API](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/package-summary.html) in the `dependencies` section: - ```bash - implementation("org.graalvm.polyglot:polyglot:25.0.2") - implementation("org.graalvm.python:python-embedding:25.0.2") - ``` - -3. Finally, replace the code in the file named _App.java_ as follows for a small Python embedding: - ```java - package interop; - - import org.graalvm.polyglot.*; - import org.graalvm.python.embedding.GraalPyResources; - - class App { - public static void main(String[] args) { - try (var context = GraalPyResources.createContext()) { - System.out.println(context.eval("python", "'Hello Python!'").asString()); - } - } - } - ``` - -4. Run the application with Gradle: - ```bash - ./gradlew run - ``` - The application prints "Hello Python!" to the console. - - > Note: The performance of the GraalPy runtime depends on the JDK in which you embed it. For more information, see [Runtime Optimization Support](https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support). - -5. Optionally, you can also use a third-party Python package. - - 5.1. In _app/build.gradle_: - - add the graalpy-gradle-plugin to the `plugins` section: - ```bash - id "org.graalvm.python" version "25.0.2" - ``` - - - configure the GraalPy Gradle plugin: - ```bash - graalPy { - packages = ["termcolor==2.2"] - } - ``` - - 5.2. In _settings.gradle_, add the following `pluginManagement` configuration. - ```bash - pluginManagement { - repositories { - gradlePluginPortal() - } - } - ``` - - 5.3. Update the file named _App.java_ as follows: - ```java - package interop; - - import org.graalvm.polyglot.*; - import org.graalvm.python.embedding.GraalPyResources; - - class App { - ... - public static void main(String[] args) { - try (Context context = GraalPyResources.contextBuilder().build()) { - String src = """ - from termcolor import colored - colored_text = colored("hello java", "red", attrs=["reverse", "blink"]) - print(colored_text) - """; - context.eval("python", src); - } - } - ``` - -See also [Embedding Build Tools](Embedding-Build-Tools.md) for more information about the GraalPy Gradle Plugin. - -## Ant, CMake, Makefile or Other Build Systems Without Direct Support for Maven Dependencies - -Some (often older) projects may be using Ant, Makefiles, CMake, or other build systems that do not directly support Maven dependencies. -Projects such as [Apache Ivy™](https://ant.apache.org/ivy/history/master/tutorial/start.html) enable such build systems to resolve Maven dependencies, but developers may have reasons not to use them. -GraalPy comes with a tool to obtain the required JAR files from Maven. - -1. Assuming there is some directory where third-party dependencies are stored for the project and that the build system is set up to put any JAR files there on the classpath, the project directory tree might look similar to this: - - ```bash - ├───lib - │ └─── ... *.jar dependencies are here - └───src - └─── ... *.java files and resources are here - ``` - -2. [Install GraalPy](Python-Runtime.md#installing-graalpy) for your system and ensure you have `graalpy` on your `PATH`. - Open a command-line interface and enter your project directory. - Then, as appropriate for your system, run one of the following commands: - - In a POSIX shell: - ```bash - export GRAALPY_HOME=$(graalpy -c 'print(__graalpython__.home)') - "${GRAALPY_HOME}/libexec/graalpy-polyglot-get" -a python -o lib -v "25.0.2" - ``` - - In PowerShell: - ```bash - $GRAALPY_HOME = graalpy -c "print(__graalpython__.home)" - & "$GRAALPY_HOME/libexec/graalpy-polyglot-get" -a python -o lib -v "25.0.2" - ``` - - These commands download all GraalPy dependencies into the _lib_ directory. - -3. Provided that your build system is set up to pick up the JAR files from _lib_, the GraalPy embedding code below should work if put in an appropriate place in the project to run as the main class. - - ```java - import org.graalvm.polyglot.*; - - public class Hello { - public static void main(String[] args) { - try (var context = Context.newBuilder().option("engine.WarnInterpreterOnly", "false").build()) { - System.out.println(context.eval("python", "'Hello Python!'").asString()); - } - } - } - ``` - -### Related Documentation - -- [Modern Python on the JVM](Python-on-JVM.md) -- [Embedding Graal languages in Java](https://www.graalvm.org/latest/reference-manual/embed-languages/) +**Linux is recommended for getting started with GraalPy.** Windows and macOS with GraalVM JDK are less well tested, and outside of those combinations only basic test coverage is provided. +As macOS and other platforms are not prioritized, some GraalPy features may not work on these platforms. +See [Test Tiers](Test-Tiers.md) for a detailed breakdown. diff --git a/docs/user/Standalone-Getting-Started.md b/docs/user/Standalone-Getting-Started.md new file mode 100644 index 0000000000..a448fd9c19 --- /dev/null +++ b/docs/user/Standalone-Getting-Started.md @@ -0,0 +1,189 @@ +# Using GraalPy as a Standalone Python Runtime + +GraalPy can be used as a standalone Python runtime, providing a drop-in replacement for CPython. +This guide covers choosing a distribution, installation, package management, basic usage, and deployment options for standalone GraalPy applications. + +## Choosing a GraalPy Distribution + +GraalPy is available in multiple distributions: + +### Distribution Options + +- **GraalPy built on Oracle GraalVM** provides the best experience with additional optimizations, significantly faster performance, and better memory efficiency. It is licensed under the [GraalVM Free Terms and Conditions (GFTC)](https://www.oracle.com/downloads/licenses/graal-free-license.html), which permits use by any user including commercial and production use. Redistribution is permitted as long as it is not for a fee. +- **GraalPy Community** is built on top of GraalVM Community Edition and is fully open source. + +### Runtime + +The standalone GraalPy runtime is compiled ahead-of-time to a native executable. +You do not need a JVM to run it, and it has a compact size, fast startup, and fast time to reach peak performance. +For Java interoperability and JVM application embedding, see [Embedding Python in Java](Embedding-Getting-Started.md). + +### Distribution Identification + +The GraalPy standalone runtimes are identified using the pattern _graalpy(-community)<python-version>-<graal-version>-<os>-<arch>_: + +| Distribution | Runtime | License | +|---------------|-----------------------------------------------------------------|---------------------------------------------------------------------------| +| **Oracle** | `graalpy---` | [GFTC](https://www.oracle.com/downloads/licenses/graal-free-license.html) | +| **Community** | `graalpy-community---` | [UPL](https://opensource.org/licenses/UPL) | + +## GraalPy Capabilities + +GraalPy provides a Python 3.12 compliant runtime. +A primary goal is to support PyTorch, SciPy, and their constituent libraries, as well as to work with other data science and machine learning libraries from the rich Python ecosystem. + +GraalPy provides the following capabilities: + +- CPython-compatible distribution for testing Python code on GraalPy. +- A [single native binary packaging mode](Python-Standalone-Applications.md) for Python applications. +- Access to GraalVM language ecosystems and tools. + +## Installation + +> **Note**: There may be a delay between GraalPy release and its availability on Pyenv. + +### Linux (Recommended Platform) + +The easiest way to install GraalPy on Linux is to use [Pyenv](https://github.com/pyenv/pyenv) (the Python version manager): + +```bash +# Update pyenv to include latest GraalPy versions (if needed) +pyenv update + +# Install GraalPy 25.0.3 +pyenv install graalpy-25.0.3 + +# Use GraalPy for the current shell session +pyenv shell graalpy-25.0.3 +``` + +#### Manual Installation (Linux) + +1. Download the appropriate binary from [GitHub releases](https://github.com/oracle/graalpython/releases): + + - AMD64: `graalpy3.12-25.1.0-linux-amd64.tar.gz` + - ARM64: `graalpy3.12-25.1.0-linux-aarch64.tar.gz` + +2. Extract and add it to your `PATH` environment variable: + + ```bash + tar -xzf graalpy3.12-25.1.0-linux-amd64.tar.gz + export PATH="$PWD/graalpy3.12-25.1.0-linux-amd64/bin:$PATH" + ``` + +### macOS + +Using Pyenv (recommended): + +```bash +# Install GraalPy 25.0.3 +pyenv install graalpy-25.0.3 + +# Use GraalPy for the current shell session +pyenv shell graalpy-25.0.3 +``` + +#### Manual Installation (macOS) + +1. Download the binary from [GitHub releases](https://github.com/oracle/graalpython/releases). + +2. Remove quarantine attribute: + + ```bash + sudo xattr -r -d com.apple.quarantine /path/to/graalpy + # For example: + sudo xattr -r -d com.apple.quarantine ~/.pyenv/versions/graalpy-25.0.3 + ``` + +3. Extract and add it to your `PATH` environment variable: + + ```bash + tar -xzf graalpy3.12-25.1.0-macos-aarch64.tar.gz + export PATH="$PWD/graalpy3.12-25.1.0-macos-aarch64/bin:$PATH" + ``` + +### Windows + +> **Warning**: The Windows distribution has more limitations than Linux or macOS. Not all features and packages may be available. + +1. Download the binary from [GitHub releases](https://github.com/oracle/graalpython/releases). + +2. Extract and add it to your `PATH` environment variable: + + ```powershell + # Extract the file and update your PATH environment variable + # to include the graalpy3.12-25.1.0-windows-amd64/bin directory + tar -xzf graalpy3.12-25.1.0-windows-amd64.zip + $env:PATH = "$PWD\graalpy3.12-25.1.0-windows-amd64\bin;$env:PATH" + ``` + +#### Known Windows Limitations + +- PowerShell works better than CMD + +## Using Virtual Environments + +The recommended way to use GraalPy is with [venv](https://docs.python.org/3/library/venv.html) virtual environments: + +### Creating a Virtual Environment + +```bash +# Create a virtual environment +graalpy -m venv ~/.virtualenvs/graalpy-25.0.3 + +# Activate the environment +source ~/.virtualenvs/graalpy-25.0.3/bin/activate +``` + +### Installing Packages + +Once in a virtual environment, you can use `pip` to install packages: + +```bash +# Install a package +pip install requests + +# Install with requirements file +pip install -r requirements.txt +``` + +> **Note**: GraalPy's `pip` implementation may choose different package versions to ensure better compatibility. + +To deactivate the virtual environment: + +```bash +# Return to your normal shell environment +deactivate +``` + +## Running Python Code + +Once installed, you can use GraalPy like any other Python interpreter: + +```bash +# Interactive REPL +graalpy + +# Run a Python script +graalpy myscript.py + +# Run a module +graalpy -m mymodule + +# Execute inline code +graalpy -c "print('Hello from GraalPy!')" +``` + +## Platform Support + +**Linux is the recommended platform** for GraalPy. +The main testing focus is: + +- **Operating System**: Oracle Linux +- **CPU Architectures**: AMD64 and ARM64 +- **Primary JDK**: Oracle GraalVM + +Windows and macOS with GraalVM JDK have less comprehensive testing. +Some GraalPy features may not work optimally on these platforms. + +See [Test Tiers](Test-Tiers.md) for a detailed breakdown of platform support. diff --git a/docs/user/Test-Tiers.md b/docs/user/Test-Tiers.md index 5e20a012b4..eb1dfedc05 100644 --- a/docs/user/Test-Tiers.md +++ b/docs/user/Test-Tiers.md @@ -12,7 +12,7 @@ Platforms are identified using the target tuple format: `[CPU architecture]-[Ope Pure Python code runs reliably on GraalPy with recent JDKs when JIT compilation is disabled. However, advanced features like native extensions, platform-specific APIs, and JIT compilation have varying support depending on your platform tier. -## Tier 1 +### Tier 1 - **Stability:** CI failures block releases. Changes which would break the main or release branches are not allowed to be merged; any breakage should be fixed or reverted immediately. - **Responsibility:** All core developers are responsible to keep main, and thus these platforms, working. @@ -23,7 +23,7 @@ Pure Python code runs reliably on GraalPy with recent JDKs when JIT compilation | amd64-linux-glibc-graal-latest | Oracle Linux 8 or similar. | | aarch64-linux-glibc-graal-latest | Oracle Linux 8 or similar. | -## Tier 2 +### Tier 2 - **Stability:** CI failures block releases. Changes which would break the main or release branches are not allowed to be merged; any breakage should be fixed or tests marked as skipped. - **Test Coverage:** Circa 10% of tests running on Tier 1 platforms may be skipped on Tier 2 platforms. @@ -33,7 +33,7 @@ Pure Python code runs reliably on GraalPy with recent JDKs when JIT compilation |-----------------------------------|-------------------------| | aarch64-macos-darwin-graal-latest | macOS on M-series CPUs. | -## Tier 3 +### Tier 3 - **Stability:** CI failures block releases. Changes which would break the main or release branches are not allowed to be merged; any breakage should be fixed or tests marked as skipped. - **Test Coverage:** Circa 25% of tests running on Tier 1 platforms may be skipped on Tier 3. @@ -48,7 +48,7 @@ Pure Python code runs reliably on GraalPy with recent JDKs when JIT compilation | amd64-macos-darwin-oracle-21 | JDK 21 is tested without JIT compilation. | | amd64-windows-msvc-oracle-21 | JDK 21 is tested without JIT compilation. | -## Tier 4 +### Tier 4 - **Stability:** CI failures do not block releases. Tests may be broken on the main and release branches. - **Test Coverage:** Smoke tests with platform-agnostic pure Python workloads are run on a regular schedule. diff --git a/docs/user/Tooling.md b/docs/user/Tooling.md index 03375b9d97..e0754c2af2 100644 --- a/docs/user/Tooling.md +++ b/docs/user/Tooling.md @@ -38,13 +38,15 @@ You can inspect variables, set watch expressions, interactively evaluate code sn ## Profiling -GraalPy provides three main profiling capabilities: CPU sampling, CPU tracing, and memory tracing. Each tool is described below. +GraalPy provides three main profiling capabilities: CPU sampling, CPU tracing, and memory tracing. +Each tool is described below. For complete options, use: `graalpy --help:tools` ### CPU Sampler -Use the `--cpusampler` command-line option to take a CPU sample. For example: +Use the `--cpusampler` command-line option to take a CPU sample. +For example: ```bash graalpy --cpusampler my_script.py ``` @@ -79,7 +81,8 @@ Thread[main,5,main] ### CPU Tracer -Use the `--cputracer --cputracer.TraceStatements` command-line options to trace CPU usage. For example: +Use the `--cputracer --cputracer.TraceStatements` command-line options to trace CPU usage. +For example: ```bash graalpy --cputracer --cputracer.TraceStatements my_script.py @@ -118,7 +121,8 @@ Tracing Histogram. Counted a total of 1135 element executions. ### Memory Tracer -Use the `--memtracer --memtracer.TraceStatements` command-line options to trace memory usage. For example: +Use the `--memtracer --memtracer.TraceStatements` command-line options to trace memory usage. +For example: ```bash graalpy --experimental-options --memtracer --memtracer.TraceStatements my_script.py @@ -363,7 +367,7 @@ You can use GraalPy in PyCharm to create a virtual environment, install packages 5. Install packages using pip or PyCharm's package manager. (For more information, see [Install, uninstall, and upgrade packages](https://www.jetbrains.com/help/pycharm/installing-uninstalling-and-upgrading-packages.html).) -6. Use the PyCharm menu items to [run your Python application](https://www.jetbrains.com/help/pycharm/running-applications.html). +6. Use the PyCharm menu items to [run your Python application](https://www.jetbrains.com/help/pycharm/running-applications.html). Alternatively, use the terminal emulator to run the `graalpy` command. ## Using Visual Studio Code with GraalPy diff --git a/docs/user/Troubleshooting.md b/docs/user/Troubleshooting.md index 68ef54171e..1b351f9183 100644 --- a/docs/user/Troubleshooting.md +++ b/docs/user/Troubleshooting.md @@ -1,12 +1,14 @@ -# GraalPy Troubleshooting +# GraalPy Embedding Troubleshooting -This guide helps you resolve common issues when using GraalPy, whether running it standalone or embedded in Java applications. +This guide helps you resolve common issues when embedding GraalPy in Java applications. +It focuses on Virtual FileSystem setup, context configuration, build tooling, and dependency alignment for JVM-based deployments. ## Virtual FileSystem Issues ### VirtualFileSystem Cannot Load Files -Some files may fail to load from the Virtual Filesystem even though they're included as resources. GraalPy automatically [extracts certain file types](Embedding-Build-Tools.md#extracting-files-from-virtual-filesystem) to the real filesystem, but you may still encounter errors. +Some files may fail to load from the Virtual Filesystem even though they're included as resources. +GraalPy automatically [extracts certain file types](Embedding-Build-Tools.md#extracting-files-from-virtual-filesystem) to the real filesystem, but you may still encounter errors. Example error: @@ -56,14 +58,15 @@ Context context = GraalPyResources.contextBuilder(externalResourceDirectoryPath) For more details about the Python resources in GraalPy Embedding please refer to the [Embedding Build Tools](Embedding-Build-Tools.md) documentation. -For more details about GraalPy context and Virtual FileSystem configuration please refer to [GraalPyResources](https://github.com/oracle/graalpython/blob/master/graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/GraalPyResources.java) and -[VirtualFileSystem](https://github.com/oracle/graalpython/blob/master/graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/VirtualFileSystem.java) javadoc. +For more details about GraalPy context and Virtual FileSystem configuration please refer to [GraalPyResources](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/GraalPyResources.html) and +[VirtualFileSystem](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/VirtualFileSystem.html) javadoc. ## POSIX Backend Issues ### Issues with Java POSIX Backend -The Virtual FileSystem relies on GraalPy's Java POSIX backend. Some Python packages bypass Python's I/O and directly access files through native extensions. +The Virtual FileSystem relies on GraalPy's Java POSIX backend. +Some Python packages bypass Python's I/O and directly access files through native extensions. Example error: @@ -107,8 +110,8 @@ Depending on how you [deploy Python resources](Embedding-Build-Tools.md#deployme For more details about the Python resources in GraalPy Embedding please refer to the [Embedding Build Tools](Embedding-Build-Tools.md) documentation. -For more details about GraalPy context and Virtual FileSystem configuration please refer to [GraalPyResources](https://github.com/oracle/graalpython/blob/master/graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/GraalPyResources.java) and -[VirtualFileSystem](https://github.com/oracle/graalpython/blob/master/graalpython/org.graalvm.python.embedding/src/org/graalvm/python/embedding/VirtualFileSystem.java) javadoc. +For more details about GraalPy context and Virtual FileSystem configuration please refer to [GraalPyResources](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/GraalPyResources.html) and +[VirtualFileSystem](https://oracle.github.io/graalpy-extensions/latest/org.graalvm.python.embedding/org/graalvm/python/embedding/VirtualFileSystem.html) javadoc. ## Import and Compatibility Issues diff --git a/docs/user/Version-Compatibility.md b/docs/user/Version-Compatibility.md new file mode 100644 index 0000000000..04370b4c7b --- /dev/null +++ b/docs/user/Version-Compatibility.md @@ -0,0 +1,9 @@ +## Version Compatibility + +The following table shows which Python versions are supported by each GraalPy release: + +| GraalPy Version | Python Version | GraalVM Platform | +| --------------- | -------------- | --------------------------------------------------------------- | +| 25.x | Python 3.12.8 | Oracle GraalVM 25.x, GraalVM Community Edition 25.x | +| 23.x | Python 3.10.8 | Oracle GraalVM for JDK 21.x, Oracle GraalVM for JDK 17.x | +| 22.x | Python 3.8.5 | GraalVM Enterprise Edition 21.3.x | \ No newline at end of file diff --git a/graalpy_virtualenv_seeder/graalpy_virtualenv_seeder/graalpy.py b/graalpy_virtualenv_seeder/graalpy_virtualenv_seeder/graalpy.py index 7d3f4ee708..1a594b8cb0 100644 --- a/graalpy_virtualenv_seeder/graalpy_virtualenv_seeder/graalpy.py +++ b/graalpy_virtualenv_seeder/graalpy_virtualenv_seeder/graalpy.py @@ -1,4 +1,4 @@ -# 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 @@ -126,6 +126,7 @@ def _native_lib(cls, lib_dir, _platform): def set_pyenv_cfg(self): # GraalPy needs an additional entry in pyvenv.cfg on Windows super().set_pyenv_cfg() + self.pyenv_cfg["base-executable"] = self.interpreter.system_executable self.pyenv_cfg["venvlauncher_command"] = self.interpreter.system_executable @@ -137,7 +138,7 @@ def set_pyenv_cfg(self): _get_default_orig = SeederSelector._get_default def _seeder_selector_get_default_override(self): - if self.interpreter.implementation == "GraalVM": + if self.interpreter.implementation.lower() in ("graalvm", "graalpy"): return "graalpy" else: return _get_default_orig() diff --git a/graalpython/AGENTS.md b/graalpython/AGENTS.md new file mode 100644 index 0000000000..3214a24ceb --- /dev/null +++ b/graalpython/AGENTS.md @@ -0,0 +1,55 @@ +# graalpython/ — SOURCE + STDLIB + TESTS + +## OVERVIEW +Main implementation tree: Java (Truffle interpreter), C (CPython C-API compatibility), and Python (stdlib overlays + tooling/tests). + +## WHERE TO LOOK +| Task | Location | Notes | +|------|----------|-------| +| Java interpreter | `com.oracle.graal.python/src/com/oracle/graal/python/{runtime,nodes,builtins}` | Most core behavior lives here. | +| Shared Java lib nodes | `com.oracle.graal.python/src/com/oracle/graal/python/lib` | Prefer adding/reusing lib nodes for common operations. | +| C-API headers + runtime | `com.oracle.graal.python.cext/include`, `.../src` | CPython-like naming; follow CPython invariants. | +| Adapted native modules | `com.oracle.graal.python.cext/modules/` | Many files are upstream-derived; patch carefully. | +| GraalPy stdlib overlays | `lib-graalpython/` | Python files executed at startup / for builtins. | +| Vendored CPython stdlib/tests | `lib-python/3/` | Treat as upstream-ish unless explicitly changing it. | +| GraalPy tests | `com.oracle.graal.python.test/src/tests/` | Python-level tests + tag files. | +| Parser components | `com.oracle.graal.python.pegparser*` | Parser implementation + golden files tests. | + +## CONVENTIONS / GOTCHAS +- This subtree contains both “source of truth” code and vendored/upstream-ish imports; keep patches minimal in `lib-python/` and large C module imports. +- Some large headers/databases (unicode tables) are generated; avoid editing them by hand unless you also update the generator pipeline. + +## RUNNING + +- Python code can be executed with GraalPy using `mx python`, invoked just like normal `python` command. The project + *must* be first built with `mx python-jvm` or you will execute stale code. Note that `mx python-jvm` just builds, it + doesn't take arguments nor execute code. + +## TESTING + +- There are multiple kinds of tests: + - GraalPy Python tests + - Our own tests in `com.oracle.graal.python.test/src/tests/` + - Executed with `mx graalpytest test_file_name` + - New test should normally be added here, unless they need to be in Java + - CPython tests, also called tagged tests + - Tests copied from upstream CPython in `lib-python/3/tests`. Should not be modified unless specifically + requested. If modified, modifications should be marked with a `# GraalPy change` comment above the changed + part. + - Executed with `mx graalpytest --tagged test_file_name` + - Uses a "tagging" system where only a subset of tests specified in tag files is normally executed. The `--all` + flag makes it ignore the tags and execute all tests. + - JUnit tests + - In `com.oracle.graal.python.test/src` and `com.oracle.graal.python.test.integration/src` + - Used primarily for testing features exposed to Java, such as embedding, instrumentation or interop. + - The tests need to be built with `mx build` prior to execution. The `mx unittest com.example.TestName` command + can be used to run individual tests. +- The `mx graalpytest` command accepts pytest‑style test selectors (e.g., `test_mod.py::TestClass::test_method`) but is + **not** a full pytest implementation. Standard pytest command‑line flags such as `-k`, `-m`, `-v`, `--maxfail` are not + supported. +- Important: The test commands don't automatically rebuild the project. It is your reponsibility to rebuild the project + using `mx python-jvm` after making changes prior to running tests otherwise the tests will run stale code. + +## ANTI-PATTERNS +- Don’t use `mxbuild/**` outputs to understand behavior; always navigate `.../src/...` trees. +- C-API: never mix `PyMem_*` / `PyObject_*` allocators with platform `malloc` family. diff --git a/graalpython/com.oracle.graal.python.annotations/pom.xml b/graalpython/com.oracle.graal.python.annotations/pom.xml new file mode 100644 index 0000000000..09392ac931 --- /dev/null +++ b/graalpython/com.oracle.graal.python.annotations/pom.xml @@ -0,0 +1,56 @@ + + + + 4.0.0 + + + org.graalvm.python.ide + graalpy-source-workspace + 1.0-SNAPSHOT + ../../pom.xml + + + com.oracle.graal.python.annotations + jar + diff --git a/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiConstant.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiConstant.java new file mode 100644 index 0000000000..6147b5378b --- /dev/null +++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiConstant.java @@ -0,0 +1,56 @@ +/* + * 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.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a Java primitive constant as the source of truth for a C {@code #define} generated into + * {@code capi.gen.h}. This is the opposite direction of {@link CApiConstants}, which marks C + * constants that should be mirrored to Java. + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface CApiConstant { +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/arrow/ArrowUtil.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiExternalFunctionSignatures.java similarity index 73% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/arrow/ArrowUtil.java rename to graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiExternalFunctionSignatures.java index cab540e404..bc093f6308 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/arrow/ArrowUtil.java +++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiExternalFunctionSignatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2026, 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 @@ -38,19 +38,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.oracle.graal.python.runtime.arrow; +package com.oracle.graal.python.annotations; -import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE; -import static com.oracle.graal.python.util.PythonUtils.callCallTarget; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; -import com.oracle.graal.python.runtime.PythonContext; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.Source; - -public class ArrowUtil { - - public static Object createNfiSignature(Node location, String methodSignature, PythonContext ctx) { - Source sigSource = Source.newBuilder(J_NFI_LANGUAGE, methodSignature, "python-nfi-signature").build(); - return callCallTarget(ctx.getEnv().parseInternal(sigSource), location); - } +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface CApiExternalFunctionSignatures { } diff --git a/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiUpcallTarget.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiUpcallTarget.java new file mode 100644 index 0000000000..a508ffa5af --- /dev/null +++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiUpcallTarget.java @@ -0,0 +1,55 @@ +/* + * 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.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a method to be a possible upcall target. This will cause the C API annotation processor to + * generate the corresponding configuration for Native Image. + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface CApiUpcallTarget { +} diff --git a/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java new file mode 100644 index 0000000000..274e9210bf --- /dev/null +++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java @@ -0,0 +1,58 @@ +/* + * 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.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.METHOD) +public @interface DowncallSignature { + NativeSimpleType returnType(); + + NativeSimpleType[] argumentTypes() default {}; + + Class retConversion() default void.class; + + Class[] argConversions() default {}; +} diff --git a/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/NativeSimpleType.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/NativeSimpleType.java new file mode 100644 index 0000000000..d4750eba04 --- /dev/null +++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/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.annotations; + +/** + * 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, + POINTER; // raw pointer represented in Java as a long +} diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/fast_subprocess.py b/graalpython/com.oracle.graal.python.benchmarks/python/fast_subprocess.py new file mode 100644 index 0000000000..1d1491ab84 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/fast_subprocess.py @@ -0,0 +1,124 @@ +# 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. + +# Utilities for benchmarking GraalPy startup. Creates a C benchmark runner that spawns +# the subprocesses to avoid counting subprocess module overhead into the benchmark +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +RUNNER_CODE = ''' +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + if (argc < 3) { + return 1; + } + int n = atoi(argv[1]); + if (n <= 0) { + return 1; + } + char **cmd_argv = &argv[2]; + for (int i = 0; i < n; ++i) { + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + return 1; + } else if (pid == 0) { + execvp(cmd_argv[0], cmd_argv); + perror("execvp"); + exit(127); // If exec fails + } else { + int status; + if (waitpid(pid, &status, 0) < 0) { + perror("waitpid"); + return 1; + } + } + } + return 0; +} +''' + +TMPDIR = tempfile.TemporaryDirectory() +RUNNER_EXE = None +ORIG_ARGV = None + + +def setup(): + global RUNNER_EXE + tmpdir = Path(TMPDIR.name) + runner_c = tmpdir / 'runner.c' + runner_c.write_text(RUNNER_CODE) + RUNNER_EXE = tmpdir / 'runner' + subprocess.check_call([os.environ.get('CC', 'gcc'), runner_c, '-O2', '-o', RUNNER_EXE]) + + global ORIG_ARGV + ORIG_ARGV = sys.orig_argv + for i, arg in enumerate(ORIG_ARGV): + if arg.endswith('.py'): + ORIG_ARGV = ORIG_ARGV[:i] + break + try: + ORIG_ARGV.remove('-snapshot-startup') + except ValueError: + pass + + +def teardown(): + TMPDIR.cleanup() + + +def run(num, code): + subprocess.check_call([ + str(RUNNER_EXE), + str(num), + *ORIG_ARGV, + "-I", # isolate from environment + "-S", # do not import site + "-B", # do not attempt to write pyc files + "-u", # do not add buffering wrappers around output streams + "-c", + code + ]) \ No newline at end of file diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/harness.py b/graalpython/com.oracle.graal.python.benchmarks/python/harness.py index 53226c94b4..b951cd548c 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 @@ -510,6 +527,7 @@ def run_benchmark(args): if GRAALPYTHON: print(f"### using bytecode DSL interpreter: {__graalpython__.is_bytecode_dsl_interpreter}") + print(f"### using forced uncached interpreter: {getattr(__graalpython__, 'is_forced_uncached_interpreter', False)}") BenchRunner(bench_file, bench_args=bench_args, iterations=iterations, warmup=warmup, warmup_runs=warmup_runs, startup=startup, live_results=live_results, self_measurement=self_measurement).run() diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/heap/allocate-dicts.py b/graalpython/com.oracle.graal.python.benchmarks/python/heap/allocate-dicts.py new file mode 100644 index 0000000000..3f9d67c4ae --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/heap/allocate-dicts.py @@ -0,0 +1,66 @@ +# 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 time + +# follows the distribution of dict sizes in import-a-lot benchmark +# +# OQL query: +# var objs = toArray(heap.objects("com.oracle.graal.python.builtins.objects.common.ObjectHashMap", false)); +# var counts = {}; +# for each (var m in objs) { +# var s = m.size; +# counts[s] = counts[s] == null ? 1 : counts[s] + 1; +# } +# map(sort(unique(map(objs, "it.size")), "lhs - rhs"), +# function (s) { return { size: s, count: counts[s] }; }) + +FACTOR = 500 +N = 256 + 8 +keys = [str(i) for i in range(N)] +small_dicts0 = [{} for i in range(22 * FACTOR)] +small_dicts1 = [{1:1} for i in range(215 * FACTOR)] +small_dicts2 = [{1:1, 2:3} for i in range(220 * FACTOR)] +small_dicts4 = [{keys[k % N]:1 for k in range(4)} for i in range(145 * FACTOR)] +small_dicts8 = [{keys[k % N]:1 for k in range(8)} for i in range(51 * FACTOR)] +dicts1 = [{(keys[k % N]):1 for k in range(8 + i % 256)} for i in range(61 * FACTOR)] + +# Sleep a bit to shake out weakref callbacks and get more measurement samples +for i in range(30): + time.sleep(0.1) 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..db0fab8e5c --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-oracledb-load.py @@ -0,0 +1,437 @@ +# 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 +# Requires python-oracledb 3.4+ + + +ensure_packages(numpy="2.2.6", cryptography="45.0.7", oracledb="3.4.2", pyarrow="20.0.0") + +import csv +from datetime import datetime +import getpass +import os +import sys +import tempfile +import time + +import pyarrow.csv + +import oracledb + +# startup database with +# $ podman run --detach --replace --name oracledb -p 1521:1521 -e ORACLE_PWD=graalpy \ +# container-registry.oracle.com/database/free:23.26.0.0 +USERNAME = os.environ.get("PYO_TEST_ADMIN_USER", "system") +CONNECTSTRING = os.environ.get("PYO_TEST_CONNECT_STRING", "127.0.0.1:1521/FREEPDB1") +PASSWORD = os.environ.get("PYO_TEST_ADMIN_PASSWORD", "graalpy") + +# ----------------------------------------------------------------------------- + +FILE_NAME = os.path.join(tempfile.gettempdir(), "graalpy-c-oracledb-load-sample.csv") +BATCH_SIZE = 2_000_000 +TABLES = ["mytabpya", "mytabdpl", "mytabpyaem", "mytabem", "mytabpd"] + +def __process_args__(batch_size=BATCH_SIZE): + return [int(str(batch_size).replace("_", ""))] + +# ----------------------------------------------------------------------------- + +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()") + + ensure_packages(pandas="2.2.3", sqlalchemy="2.0.45") + import pandas + from sqlalchemy import create_engine + + 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__(batch_size=BATCH_SIZE): + global BATCH_SIZE + BATCH_SIZE = batch_size + + # 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 + timeout = float(os.environ.get("GRAALPY_ORACLEDB_WAIT_TIMEOUT", "0")) + deadline = time.monotonic() + timeout + attempt = 0 + while True: + try: + CONNECTION = oracledb.connect(user=USERNAME, password=PASSWORD, dsn=CONNECTSTRING) + break + except oracledb.Error: + if time.monotonic() >= deadline: + raise + attempt += 1 + print(f"Waiting for Oracle Database at {CONNECTSTRING} (attempt {attempt})") + time.sleep(5) + + quiet_seconds = float(os.environ.get("GRAALPY_ORACLEDB_QUIET_SECONDS", "0")) + if quiet_seconds > 0: + print(f"Waiting {quiet_seconds:g} seconds for Oracle Database to settle") + time.sleep(quiet_seconds) + + +def __benchmark__(batch_size=BATCH_SIZE): + assert batch_size == BATCH_SIZE + t1 = TABLES[0] + createtab(CONNECTION, t1) + pya(CONNECTION, t1) + checkrowcount(CONNECTION, t1) + + t2 = TABLES[1] + # createtab(CONNECTION, t2) + # dpl(CONNECTION, t2) + # checkrowcount(CONNECTION, t2) + + t3 = TABLES[2] + # createtab(CONNECTION, t3) + # pyaem(CONNECTION, t3) + # checkrowcount(CONNECTION, t3) + + t4 = TABLES[3] + # createtab(CONNECTION, t4) + # em(CONNECTION, t4) + # checkrowcount(CONNECTION, t4) + + t5 = TABLES[4] + # 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): + if CONNECTION is not None: + droptabs(CONNECTION, TABLES) + + +def __teardown__(): + global CONNECTION + if CONNECTION is not None: + CONNECTION.close() + CONNECTION = None + + +if __name__ == "__main__": + if len(sys.argv) > 1: + BATCH_SIZE = int(sys.argv[1]) + __setup__(BATCH_SIZE) + print("\nCompare end-to-end times for reading a " + "CSV file (number, date, string) in chunks and inserting into the Database") + __benchmark__(BATCH_SIZE) + __cleanup__() + __teardown__() 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/matplotlib/3d_surface_wireframe.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/3d_surface_wireframe.py new file mode 100644 index 0000000000..96ac2bdb65 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/3d_surface_wireframe.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# 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. + +""" +3D plotting example: surface, wireframe, and contour projections. +Saves to PDF. +""" + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "surface_3d.pdf" + + # Domain and function + x = np.linspace(-4, 4, 200) + y = np.linspace(-4, 4, 200) + X, Y = np.meshgrid(x, y) + + R = np.sqrt(X**2 + Y**2) + 1e-12 + Z = np.sin(R) / R + 0.15 * np.cos(3*X) * np.sin(3*Y) / (1 + 0.5 * (X**2 + Y**2)) + + fig = plt.figure(figsize=(7.5, 5.8), dpi=150) + ax = fig.add_subplot(111, projection="3d") + + # Surface with colormap + surf = ax.plot_surface(X, Y, Z, cmap="viridis", linewidth=0, antialiased=True, alpha=0.95) + + # Wireframe overlay (sparser grid to avoid clutter) + step = 10 + ax.plot_wireframe(X[::step, ::step], Y[::step, ::step], Z[::step, ::step], + rstride=1, cstride=1, color="k", linewidth=0.3, alpha=0.5) + + # Contour projections on Z, X, and Y planes + z_offset = Z.min() - 0.4 + ax.contour(X, Y, Z, zdir="z", offset=z_offset, cmap="viridis", levels=18, linewidths=0.8) + + x_offset = x.min() - 0.6 + ax.contour(X, Y, Z, zdir="x", offset=x_offset, cmap="magma", levels=14, linewidths=0.7) + + y_offset = y.max() + 0.6 + ax.contour(X, Y, Z, zdir="y", offset=y_offset, cmap="plasma", levels=14, linewidths=0.7) + + # Axes labels and limits + ax.set_xlabel("X") + ax.set_ylabel("Y") + ax.set_zlabel("Z") + + ax.set_xlim(x_offset, x.max()) + ax.set_ylim(y.min(), y_offset) + ax.set_zlim(z_offset, Z.max()) + + # Colorbar + cb = fig.colorbar(surf, ax=ax, shrink=0.6, aspect=12, pad=0.08) + cb.set_label("Z value") + + # View angle + ax.view_init(elev=25, azim=-55) + + ax.set_title("3D Surface + Wireframe + Contour Projections") + fig.tight_layout() + + fig.savefig(out_path, format="pdf") + plt.close(fig) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/OWNERS.toml b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/OWNERS.toml new file mode 100644 index 0000000000..34cb11f338 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/OWNERS.toml @@ -0,0 +1,6 @@ +[[rule]] +files = "*" +any = [ + "francois.farquet@oracle.com", + "andrija.kolic@oracle.com", +] diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/categorical_bar_and_box.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/categorical_bar_and_box.py new file mode 100644 index 0000000000..6fa26ce37a --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/categorical_bar_and_box.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# 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. + +""" +Categorical plots: grouped bar chart with error bars and a boxplot. +Saves a multi-page PDF using PdfPages. +""" + + +def _colorize_boxplot(bp, facecolor="#1f77b4", edgecolor="black", alpha=0.6): + for box in bp["boxes"]: + box.set(facecolor=facecolor, edgecolor=edgecolor, alpha=alpha) + for median in bp["medians"]: + median.set(color="black", linewidth=1.2) + for whisker in bp["whiskers"]: + whisker.set(color=edgecolor, linewidth=1.0) + for cap in bp["caps"]: + cap.set(color=edgecolor, linewidth=1.0) + for flier in bp["fliers"]: + flier.set(marker="o", markersize=3, markerfacecolor="white", markeredgecolor=edgecolor, alpha=0.7) + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + from matplotlib.backends.backend_pdf import PdfPages + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "categorical_plots.pdf" + + rng = np.random.default_rng(2024) + categories = ["A", "B", "C", "D"] + n = len(categories) + + with PdfPages(out_path) as pdf: + # Page 1: Grouped bar chart with error bars + x = np.arange(n) + bar_w = 0.35 + + means1 = rng.normal(3.0, 0.4, n) + errs1 = rng.uniform(0.1, 0.4, n) + + means2 = rng.normal(2.2, 0.5, n) + errs2 = rng.uniform(0.1, 0.4, n) + + fig1, ax1 = plt.subplots(figsize=(7, 4), dpi=150) + b1 = ax1.bar(x - bar_w / 2, means1, yerr=errs1, width=bar_w, capsize=3, + label="Series 1", color="#1f77b4", edgecolor="black", alpha=0.85) + b2 = ax1.bar(x + bar_w / 2, means2, yerr=errs2, width=bar_w, capsize=3, + label="Series 2", color="#ff7f0e", edgecolor="black", alpha=0.85) + + ax1.set_xticks(x, categories) + ax1.set_ylabel("Value") + ax1.set_title("Grouped Bar Chart with Error Bars") + ax1.grid(axis="y", linestyle="--", alpha=0.35) + ax1.legend(loc="best") + + # Annotate bars with heights + for bars in (b1, b2): + for rect in bars: + h = rect.get_height() + ax1.text(rect.get_x() + rect.get_width() / 2.0, h + 0.05, + f"{h:.2f}", ha="center", va="bottom", fontsize=8, rotation=0) + + fig1.tight_layout() + pdf.savefig(fig1) + plt.close(fig1) + + # Page 2: Boxplot across categories + # Generate some synthetic distributions with varying mean/variance + mus = [2.8, 3.2, 2.5, 3.5] + sigmas = [0.50, 0.60, 0.45, 0.55] + data = [rng.normal(loc=m, scale=s, size=400) for m, s in zip(mus, sigmas)] + + fig2, ax2 = plt.subplots(figsize=(7, 4), dpi=150) + bp = ax2.boxplot( + data, + labels=categories, + widths=0.6, + patch_artist=True, + showfliers=True, + whis=(5, 95), + ) + # Colorize boxes with a palette + palette = ["#1f77b4", "#ff7f0e", "#2ca02c", "#9467bd"] + for box, color in zip(bp["boxes"], palette): + box.set(facecolor=color, edgecolor="black", alpha=0.6) + + # Style the rest + for median in bp["medians"]: + median.set(color="black", linewidth=1.4) + for whisker in bp["whiskers"]: + whisker.set(color="black", linewidth=1.0) + for cap in bp["caps"]: + cap.set(color="black", linewidth=1.0) + for flier in bp["fliers"]: + flier.set(marker="o", markersize=3, markerfacecolor="white", markeredgecolor="black", alpha=0.7) + + ax2.set_title("Boxplot by Category") + ax2.set_ylabel("Distribution") + ax2.grid(axis="y", linestyle="--", alpha=0.35) + fig2.tight_layout() + + pdf.savefig(fig2) + plt.close(fig2) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/distributions_hist_2d.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/distributions_hist_2d.py new file mode 100644 index 0000000000..092d2aa746 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/distributions_hist_2d.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 +# 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. + +""" +Distribution plots: 1D histogram, 2D histogram, and hexbin with colorbars. +""" + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "distributions_2d.pdf" + + rng = np.random.default_rng(7) + n = 6000 + + # Create two correlated variables + x = rng.normal(0.0, 1.0, n) + y = 0.65 * x + rng.normal(0.0, 0.8, n) + + fig, axs = plt.subplots(1, 3, figsize=(12, 4), dpi=150) + + # 1) 1D histograms overlayed + ax = axs[0] + ax.hist(x, bins=40, alpha=0.8, label="x", color="#1f77b4", edgecolor="white") + ax.hist(y, bins=40, alpha=0.6, label="y", color="#ff7f0e", edgecolor="white") + ax.set_title("1D Histograms") + ax.set_xlabel("Value") + ax.set_ylabel("Frequency") + ax.grid(True, linestyle="--", alpha=0.3) + ax.legend(loc="best") + + # 2) 2D histogram via pcolormesh + ax = axs[1] + H, xedges, yedges = np.histogram2d(x, y, bins=60) + X, Y = np.meshgrid(xedges, yedges) + pcm = ax.pcolormesh(X, Y, H.T, cmap="viridis", shading="auto") + ax.set_title("2D Histogram") + ax.set_xlabel("x") + ax.set_ylabel("y") + cb = fig.colorbar(pcm, ax=ax) + cb.set_label("Count") + ax.grid(False) + + # 3) Hexbin with log color scale + ax = axs[2] + hb = ax.hexbin(x, y, gridsize=45, cmap="plasma", mincnt=1, bins="log") + ax.set_title("Hexbin (log density)") + ax.set_xlabel("x") + ax.set_ylabel("y") + cb = fig.colorbar(hb, ax=ax) + cb.set_label("log10(count)") + ax.grid(False) + + fig.suptitle("Distributions: 1D and 2D Density", fontsize=14) + fig.tight_layout(rect=[0, 0.03, 1, 0.95]) + + fig.savefig(out_path, format="pdf") + plt.close(fig) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/polar_quiver_stream.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/polar_quiver_stream.py new file mode 100644 index 0000000000..192d984f63 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/polar_quiver_stream.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# 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. + +""" +Vector and polar plots: polar plot, quiver, and streamplot on separate pages. +Saves a multi-page PDF using PdfPages. +""" + + +def page_polar(ax): + # Polar demo with multiple radii and an area fill + import numpy as np + theta = np.linspace(0, 2*np.pi, 512) + r1 = 1.0 + 0.3*np.sin(5*theta) + r2 = 0.7 + 0.2*np.cos(3*theta + 0.5) + + ax.plot(theta, r1, color="#1f77b4", linewidth=2.0, label="r1(θ) = 1 + 0.3 sin(5θ)") + ax.plot(theta, r2, color="#ff7f0e", linewidth=2.0, linestyle="--", label="r2(θ) = 0.7 + 0.2 cos(3θ+0.5)") + ax.fill_between(theta, r2, color="#ff7f0e", alpha=0.25, step="mid") + ax.set_theta_zero_location("N") + ax.set_theta_direction(-1) + ax.set_title("Polar Plot", va="bottom") + ax.grid(True, alpha=0.4) + ax.legend(loc="upper right", bbox_to_anchor=(1.25, 1.15), frameon=False) + + +def page_quiver(ax): + # Quiver plot for a simple rotational vector field + import numpy as np + n = 25 + x = np.linspace(-2.0, 2.0, n) + y = np.linspace(-2.0, 2.0, n) + X, Y = np.meshgrid(x, y) + + # Vector field: rotation around origin + U = -Y + V = X + speed = np.hypot(U, V) + + q = ax.quiver(X, Y, U, V, speed, cmap="viridis", pivot="mid", angles="xy", scale=35, width=0.006) + ax.set_aspect("equal", adjustable="box") + ax.set_title("Quiver: Rotational Field") + ax.set_xlabel("x") + ax.set_ylabel("y") + cb = ax.figure.colorbar(q, ax=ax, pad=0.01) + cb.set_label("|v|") + ax.grid(True, linestyle="--", alpha=0.3) + + +def page_streamplot(ax): + # Streamplot with linewidth and color mapped to speed + import numpy as np + x = np.linspace(-3.0, 3.0, 200) + y = np.linspace(-3.0, 3.0, 200) + X, Y = np.meshgrid(x, y) + + # Double-vortex-like field + U = 1 - (X**2) + (Y**2) + V = -2*X*Y + speed = np.sqrt(U**2 + V**2) + + lw = 1.5 * speed / (speed.max() + 1e-12) + strm = ax.streamplot(X, Y, U, V, color=speed, linewidth=lw, cmap="plasma", density=1.4, arrowsize=1.2) + ax.set_aspect("equal", adjustable="box") + ax.set_title("Streamplot: Speed-coded") + ax.set_xlabel("x") + ax.set_ylabel("y") + cb = ax.figure.colorbar(strm.lines, ax=ax, pad=0.01) + cb.set_label("|v|") + ax.grid(True, linestyle="--", alpha=0.25) + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + from matplotlib.backends.backend_pdf import PdfPages + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "vector_and_polar.pdf" + + with PdfPages(out_path) as pdf: + # Page 1: Polar + fig1 = plt.figure(figsize=(6.2, 5.5), dpi=150) + ax1 = fig1.add_subplot(111, projection="polar") + page_polar(ax1) + fig1.tight_layout() + pdf.savefig(fig1) + plt.close(fig1) + + # Page 2: Quiver + fig2, ax2 = plt.subplots(figsize=(6.2, 5.0), dpi=150) + page_quiver(ax2) + fig2.tight_layout() + pdf.savefig(fig2) + plt.close(fig2) + + # Page 3: Streamplot + fig3, ax3 = plt.subplots(figsize=(6.6, 5.2), dpi=150) + page_streamplot(ax3) + fig3.tight_layout() + pdf.savefig(fig3) + plt.close(fig3) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/simple_line_plot.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/simple_line_plot.py new file mode 100644 index 0000000000..8333b80f53 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/simple_line_plot.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# 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. + +""" +Simple line plot example that saves to PDF. +""" + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "simple_line_plot.pdf" + + # Reproducible data + rng = np.random.default_rng(42) + x = np.linspace(0.0, 10.0, 200) + y = np.sin(x) + 0.15 * rng.standard_normal(x.size) + + plt.figure(figsize=(6, 4), dpi=150) + plt.plot(x, np.sin(x), label="sin(x)", color="#1f77b4", linewidth=2.0) + plt.scatter(x[::8], y[::8], label="samples", color="#ff7f0e", s=15, alpha=0.85) + plt.title("Simple Line + Sampled Points") + plt.xlabel("x") + plt.ylabel("y") + plt.grid(True, linestyle="--", alpha=0.4) + plt.legend(loc="best") + plt.tight_layout() + + plt.savefig(out_path, format="pdf") + plt.close() + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/subplots_and_styles.py b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/subplots_and_styles.py new file mode 100644 index 0000000000..011a2b612b --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/matplotlib/subplots_and_styles.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# 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. + +""" +Subplots and style variations; saves a multi-panel PDF. +""" + + +def run(): + # Execute all imports inside the `run` method so they're measured + from pathlib import Path + # Use a non-interactive backend to work in headless environments + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + import numpy as np + + # Ensure we have version info in the logs + print(f"Using matplotlib version '{matplotlib.__version__}'") + print(f"Using numpy version '{np.__version__}'") + + out_path = Path(__file__).parent / "subplots_and_styles.pdf" + + rng = np.random.default_rng(123) + x = np.linspace(0, 2*np.pi, 200) + y1 = np.sin(x) + y2 = np.cos(x) + y3 = np.sin(2*x) * np.exp(-0.3*x) + y4 = rng.normal(loc=0.0, scale=1.0, size=200) + + fig, axs = plt.subplots(2, 2, figsize=(8, 6), dpi=150) + + # 1) Basic line styles + ax = axs[0, 0] + ax.plot(x, y1, label="sin(x)", color="#1f77b4", linewidth=2.0) + ax.plot(x, y2, label="cos(x)", color="#ff7f0e", linestyle="--", linewidth=2.0) + ax.set_title("Line Styles") + ax.set_xlabel("x") + ax.set_ylabel("y") + ax.grid(True, linestyle=":", alpha=0.5) + ax.legend(loc="best") + + # 2) Markers and transparency + ax = axs[0, 1] + ax.plot(x, y3, color="#2ca02c", linewidth=1.5) + ax.scatter(x[::10], y3[::10], color="#d62728", s=20, alpha=0.8, label="samples") + ax.set_title("Markers and Decay") + ax.annotate("decay", xy=(2.0, y3[np.searchsorted(x, 2.0)]), xytext=(3.5, 0.8), + arrowprops=dict(arrowstyle="->", color="gray"), color="gray") + ax.grid(True, alpha=0.4) + ax.legend(loc="best") + + # 3) Simple bar chart + ax = axs[1, 0] + categories = ["A", "B", "C", "D", "E"] + values = np.abs(rng.normal(3.0, 1.0, size=len(categories))) + bars = ax.bar(categories, values, color="#9467bd", edgecolor="black", alpha=0.85) + for b in bars: + ax.text(b.get_x() + b.get_width()/2, b.get_height() + 0.05, + f"{b.get_height():.1f}", ha="center", va="bottom", fontsize=8) + ax.set_title("Bar Chart") + ax.set_ylabel("Value") + ax.set_ylim(0, max(values) * 1.2) + ax.grid(axis="y", linestyle="--", alpha=0.3) + + # 4) Histogram with style + ax = axs[1, 1] + ax.hist(y4, bins=20, color="#8c564b", edgecolor="white", alpha=0.9) + ax.set_title("Histogram") + ax.set_xlabel("Value") + ax.set_ylabel("Frequency") + ax.grid(True, linestyle="--", alpha=0.3) + + fig.suptitle("Subplots and Styles", fontsize=14) + fig.tight_layout(rect=[0, 0.03, 1, 0.95]) + + fig.savefig(out_path, format="pdf") + plt.close(fig) + + +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-classmethod.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-classmethod.py index 37922366fc..304d36c236 100644 --- a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-classmethod.py +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-classmethod.py @@ -1,4 +1,4 @@ -# 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 @@ -99,14 +99,14 @@ static PyModuleDef c_classmethod_module = { PyModuleDef_HEAD_INIT, - "c_classmethod_module", + "c_call_classmethod", "", -1, NULL, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC -PyInit_c_classmethod(void) +PyInit_c_call_classmethod(void) { PyObject* m; @@ -131,8 +131,8 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from harness import ccompile -ccompile("c_classmethod", code) -from c_classmethod import NativeCustomType +ccompile("c_call_classmethod", code) +from c_call_classmethod import NativeCustomType # ~igv~: function_root_count_at def count(num): 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-issubtype-monorphic.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-issubtype-monorphic.py index be6a071738..18b3aa51e8 100644 --- a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-issubtype-monorphic.py +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-issubtype-monorphic.py @@ -1,4 +1,4 @@ -# 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 @@ -98,14 +98,14 @@ static PyModuleDef c_classmethod_module = { PyModuleDef_HEAD_INIT, - "c_classmethod_module", + "c_issubtype_monorphic", 0, -1, NULL, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC -PyInit_c_classmethod(void) +PyInit_c_issubtype_monorphic(void) { PyObject* m; @@ -130,8 +130,8 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from harness import ccompile -ccompile("c_classmethod", code) -from c_classmethod import NativeCustomType +ccompile("c_issubtype_monorphic", code) +from c_issubtype_monorphic import NativeCustomType # igv: function_root_count_at def count(num): diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-issubtype-polymorphic.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-issubtype-polymorphic.py index 1f975a037e..fe5373923c 100644 --- a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-issubtype-polymorphic.py +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-issubtype-polymorphic.py @@ -1,4 +1,4 @@ -# 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 @@ -99,14 +99,14 @@ static PyModuleDef c_classmethod_module = { PyModuleDef_HEAD_INIT, - "c_classmethod_module", + "c_issubtype_polymorphic", 0, -1, NULL, NULL, NULL, NULL, NULL }; PyMODINIT_FUNC -PyInit_c_classmethod(void) +PyInit_c_issubtype_polymorphic(void) { PyObject* m; @@ -131,8 +131,8 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from harness import ccompile -ccompile("c_classmethod", code) -from c_classmethod import NativeCustomType +ccompile("c_issubtype_polymorphic", code) +from c_issubtype_polymorphic import NativeCustomType class X(): pass 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.benchmarks/python/micro/jsonrpc-pipe.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/jsonrpc-pipe.py new file mode 100644 index 0000000000..f3762c5466 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/jsonrpc-pipe.py @@ -0,0 +1,455 @@ +# 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. + +from __future__ import annotations + +import argparse +import io +import json +import os +import re +import subprocess +import sys +import time + + +EMAIL_RE = re.compile(r"\s+") +NON_DIGIT_RE = re.compile(r"\D+") +_STATE = None + + +class Endpoint: + def __init__(self, mode, reader, writer, closeables=()): + self.mode = mode + self.reader = reader + self.writer = writer + self.closeables = closeables + + def write_message(self, message): + line = json.dumps(message, separators=(",", ":")) + if self.mode == "text": + self.writer.write(line) + self.writer.write("\n") + self.writer.flush() + return + payload = (line + "\n").encode("utf-8") + if self.mode == "buffer": + self.writer.write(payload) + self.writer.flush() + return + write_all(self.writer, payload) + + def read_message(self): + if self.mode == "text": + line = self.reader.readline() + else: + data = self.reader.readline() + line = data.decode("utf-8") if data else "" + if not line: + raise EOFError("unexpected EOF while reading line") + return json.loads(line) + + def close(self): + streams = self.closeables if self.closeables else (self.reader, self.writer) + for stream in streams: + if hasattr(stream, "close"): + try: + stream.close() + except OSError: + pass + + +class FDLineReader: + def __init__(self, fd): + self.fd = fd + self.pending = bytearray() + + def readline(self): + while True: + newline = self.pending.find(b"\n") + if newline >= 0: + line = bytes(self.pending[: newline + 1]) + del self.pending[: newline + 1] + return line + chunk = os.read(self.fd, 4096) + if not chunk: + if not self.pending: + return b"" + line = bytes(self.pending) + self.pending.clear() + return line + self.pending.extend(chunk) + + +class State: + def __init__(self, roundtrips, client_io, worker_io, workload, payload_bytes, batch_size): + self.roundtrips = roundtrips + self.client_io = client_io + self.worker_io = worker_io + self.workload = workload + self.payload_bytes = payload_bytes + self.batch_size = batch_size + self.next_request_id = 1 + self.process = None + self.endpoint = None + + +def write_all(fd, data): + view = memoryview(data) + while view: + written = os.write(fd, view) + view = view[written:] + + +def create_text_endpoint(read_raw, write_raw): + reader_buffer = io.BufferedReader(read_raw, buffer_size=8192) + writer_buffer = io.BufferedWriter(write_raw, buffer_size=8192) + reader = io.TextIOWrapper(reader_buffer, encoding="utf-8", newline=None) + writer = io.TextIOWrapper(writer_buffer, encoding="utf-8", newline="\n", line_buffering=False, write_through=False) + return Endpoint("text", reader, writer) + + +def create_buffer_endpoint(read_raw, write_raw): + reader = io.BufferedReader(read_raw, buffer_size=8192) + writer = io.BufferedWriter(write_raw, buffer_size=8192) + return Endpoint("buffer", reader, writer) + + +def create_fd_endpoint(read_fd, write_fd, closeables=()): + return Endpoint("fd", FDLineReader(read_fd), write_fd, closeables) + + +def create_parent_endpoint(process, mode): + if mode == "text": + return create_text_endpoint(process.stdout, process.stdin) + if mode == "buffer": + return create_buffer_endpoint(process.stdout, process.stdin) + return create_fd_endpoint(process.stdout.fileno(), process.stdin.fileno(), (process.stdout, process.stdin)) + + +def create_worker_endpoint(mode): + if mode == "text": + return Endpoint("text", sys.stdin, sys.stdout) + if mode == "buffer": + return Endpoint("buffer", sys.stdin.buffer, sys.stdout.buffer) + return create_fd_endpoint(0, 1) + + +def normalize_email(value): + return EMAIL_RE.sub("", value.strip().lower()) + + +def normalize_phone(value): + digits = NON_DIGIT_RE.sub("", value) + if digits.startswith("00"): + digits = digits[2:] + return digits + + +def mask_email(value): + name, _, domain = value.partition("@") + if not domain: + return "***" + return "%s***@%s" % (name[:1], domain) + + +def mask_phone(value): + if len(value) <= 4: + return "*" * len(value) + return "*" * (len(value) - 4) + value[-4:] + + +def mask_row(row): + email = normalize_email(str(row.get("email", ""))) + phone = normalize_phone(str(row.get("phone", ""))) + return { + "email_normalized": email, + "phone_normalized": phone, + "email_masked": mask_email(email) if email else None, + "phone_masked": mask_phone(phone) if phone else None, + "region": str(row.get("region", "")).upper(), + "source": str(row.get("source", "")).lower(), + } + + +def make_echo_payload(payload_bytes): + if payload_bytes <= 0: + return "" + unit = "payload-" + return (unit * ((payload_bytes // len(unit)) + 1))[:payload_bytes] + + +def make_mask_row(index, payload_bytes): + suffix = make_echo_payload(max(payload_bytes, 8)) + return { + "email": " User%s.%s@Example.COM " % (index, suffix), + "phone": "+49 (170) %04d-%s" % (index, suffix[:8]), + "region": "eu", + "source": "microbench", + } + + +def build_request(kind, request_id, payload_bytes, batch_size): + if kind == "health": + method = "health" + params = {} + elif kind == "echo": + method = "echo" + params = {"payload": make_echo_payload(payload_bytes)} + elif kind == "mask": + method = "mask" + params = make_mask_row(request_id, payload_bytes) + elif kind == "mask_batch": + method = "mask_batch" + params = {"rows": [make_mask_row(request_id + i, payload_bytes) for i in range(batch_size)]} + else: + raise AssertionError("unsupported request kind: %s" % kind) + return {"jsonrpc": "2.0", "id": request_id, "method": method, "params": params} + + +def handle_request(message): + request_id = message.get("id") + method = message.get("method") + params = message.get("params", {}) + if method == "health": + result = {"ok": True, "worker": "jsonrpc-pipe", "protocol": "json-rpc-2.0-ndjson"} + elif method == "echo": + payload = str(dict(params).get("payload", "")) + result = {"ok": True, "echo": payload, "size": len(payload)} + elif method == "mask": + result = {"ok": True, "normalized": mask_row(dict(params))} + elif method == "mask_batch": + rows = [mask_row(dict(row)) for row in dict(params).get("rows", [])] + result = {"ok": True, "normalized": rows, "count": len(rows)} + else: + return {"jsonrpc": "2.0", "id": request_id, "error": {"code": -32601, "message": "method not found"}} + return {"jsonrpc": "2.0", "id": request_id, "result": result} + + +def validate_response(request, response, kind, payload_bytes, batch_size): + if response.get("id") != request["id"]: + raise AssertionError("mismatched response id") + if "error" in response: + raise AssertionError("worker returned error: %s" % (response["error"],)) + result = response.get("result") + if not isinstance(result, dict) or not result.get("ok"): + raise AssertionError("unexpected response payload: %s" % (response,)) + if kind == "echo": + if result.get("echo") != make_echo_payload(payload_bytes): + raise AssertionError("echo payload mismatch") + elif kind == "mask": + expected = mask_row(make_mask_row(int(request["id"]), payload_bytes)) + if result.get("normalized") != expected: + raise AssertionError("mask result mismatch") + elif kind == "mask_batch": + expected_rows = [mask_row(make_mask_row(int(request["id"]) + i, payload_bytes)) for i in range(batch_size)] + if result.get("count") != batch_size or result.get("normalized") != expected_rows: + raise AssertionError("mask_batch result mismatch") + + +def run_roundtrips(state): + completed = 0 + for _ in range(state.roundtrips): + request = build_request(state.workload, state.next_request_id, state.payload_bytes, state.batch_size) + state.next_request_id += 1 + state.endpoint.write_message(request) + response = state.endpoint.read_message() + validate_response(request, response, state.workload, state.payload_bytes, state.batch_size) + completed += 1 + return completed + + +def parse_int(value): + if isinstance(value, int): + return value + return int(str(value).replace("_", "")) + + +def get_subprocess_launcher_args(): + orig_argv = getattr(sys, "orig_argv", None) + if not orig_argv: + return [sys.executable] + + main_argv0 = sys.argv[0] + for i, arg in enumerate(orig_argv[1:], start=1): + if arg == main_argv0: + return [sys.executable, *orig_argv[1:i]] + + launcher_args = [sys.executable] + i = 1 + while i < len(orig_argv): + arg = orig_argv[i] + if not arg.startswith("-"): + break + launcher_args.append(arg) + if arg == "-X" and i + 1 < len(orig_argv): + i += 1 + launcher_args.append(orig_argv[i]) + i += 1 + return launcher_args + + +def __process_args__(roundtrips=500, client_io="text", worker_io="text", workload="mask", payload_bytes=64, batch_size=8): + return [ + parse_int(roundtrips), + str(client_io), + str(worker_io), + str(workload), + parse_int(payload_bytes), + parse_int(batch_size), + ] + + +def __setup__(roundtrips=500, client_io="text", worker_io="text", workload="mask", payload_bytes=64, batch_size=8): + global _STATE + __teardown__() + state = State(roundtrips, client_io, worker_io, workload, payload_bytes, batch_size) + command = [ + *get_subprocess_launcher_args(), + __file__, + "--worker", + "--worker-io=%s" % worker_io, + ] + process = subprocess.Popen( + command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + bufsize=0, + ) + state.process = process + state.endpoint = create_parent_endpoint(process, client_io) + _STATE = state + + +def __benchmark__(roundtrips=500, client_io="text", worker_io="text", workload="mask", payload_bytes=64, batch_size=8): + if _STATE is None: + __setup__(roundtrips, client_io, worker_io, workload, payload_bytes, batch_size) + return run_roundtrips(_STATE) + + +def __teardown__(): + global _STATE + state = _STATE + _STATE = None + if state is None: + return + try: + if state.endpoint is not None: + state.endpoint.close() + finally: + if state.process is not None: + stderr = b"" + try: + stderr = state.process.stderr.read() if state.process.stderr is not None else b"" + except OSError: + pass + return_code = state.process.wait() + if return_code != 0: + raise RuntimeError("worker exited with status %d: %s" % (return_code, stderr.decode("utf-8", errors="replace"))) + + +def run_worker(worker_io): + endpoint = create_worker_endpoint(worker_io) + try: + while True: + try: + request = endpoint.read_message() + except EOFError: + return 0 + endpoint.write_message(handle_request(request)) + finally: + endpoint.close() + + +def run_direct(roundtrips, client_io, worker_io, workload, payload_bytes, batch_size): + start = time.perf_counter() + __setup__(roundtrips, client_io, worker_io, workload, payload_bytes, batch_size) + try: + completed = __benchmark__(roundtrips, client_io, worker_io, workload, payload_bytes, batch_size) + finally: + __teardown__() + wall = time.perf_counter() - start + print("roundtrips=%d" % completed) + print("wall_s=%s" % wall) + print("throughput_ops_s=%s" % (completed / wall if wall else 0.0)) + return 0 + + +def main(argv=None): + parser = argparse.ArgumentParser(description="Strict JSON-RPC-like pipe roundtrip microbenchmark.") + parser.add_argument("--worker", action="store_true") + parser.add_argument("--worker-io", choices=("text", "buffer", "fd"), default="text") + parser.add_argument("--roundtrips", type=parse_int, default=500) + parser.add_argument("--client-io", choices=("text", "buffer", "fd"), default="text") + parser.add_argument("--workload", choices=("health", "echo", "mask", "mask_batch"), default="mask") + parser.add_argument("--payload-bytes", type=parse_int, default=64) + parser.add_argument("--batch-size", type=parse_int, default=8) + args = parser.parse_args(argv) + if args.worker: + return run_worker(args.worker_io) + return run_direct(args.roundtrips, args.client_io, args.worker_io, args.workload, args.payload_bytes, args.batch_size) + + +def run(): + __setup__() + try: + __benchmark__() + finally: + __teardown__() + + +def warmupIterations(): + return 5 + + +def iterations(): + return 10 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0, + "upper-threshold": 0.3, + } + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/list-comp2.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/list-comp2.py new file mode 100644 index 0000000000..e6048d9b53 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/list-comp2.py @@ -0,0 +1,39 @@ +# Copyright (c) 2017, 2026, Oracle and/or its affiliates. +# Copyright (c) 2013, Regents of the University of California +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# micro benchmark: list comprehension + + +def make_lists(num): + for i in range(num): + ll = [i for i in range(10)] + + return ll[-1] + + +def measure(num): + return make_lists(num) # 50000 + + +def __benchmark__(num=5000): + return measure(num) diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/nano-attribute.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/nano-attribute.py new file mode 100644 index 0000000000..c07d98ca1b --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/nano-attribute.py @@ -0,0 +1,50 @@ +# Copyright (c) 2025, 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. +class T(): + def __init__(self): + self.foo = 1 + +def measure(i,j): + o = T() + while i < j: + i = i + o.foo + return i + +def __benchmark__(*args): + return measure(0, 100000000) diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/nano-property.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/nano-property.py new file mode 100644 index 0000000000..682a30074c --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/nano-property.py @@ -0,0 +1,51 @@ +# Copyright (c) 2025, 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. +class T(): + @property + def foo(self): + return 1 + +def measure(i, j): + o = T() + while i < j: + i = i + o.foo + return i + +def __benchmark__(*args): + return measure(0, 100000000) diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup-imports.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup-imports.py new file mode 100644 index 0000000000..158e9ed895 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup-imports.py @@ -0,0 +1,56 @@ +# 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. +import os +import subprocess +import sys +import fast_subprocess + +def __setup__(*args): + fast_subprocess.setup() + + +def __teardown__(): + fast_subprocess.teardown() + + +def __benchmark__(num=1000000): + fast_subprocess.run(num, "import threading;import contextlib;import contextvars;import decimal;" + "import ast;import asyncio;import argparse;import base64;import bisect;import calendar;" + "import configparser;import copyreg;import dataclasses;import enum;import fractions;" + "import glob;import hashlib;import os;import typing;import tomllib") diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup.py index 07d2c75dee..18352db9bd 100644 --- a/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup.py +++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/startup.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 @@ -39,84 +39,15 @@ import os import subprocess import sys -import tempfile -from pathlib import Path - -# Use a C runner to spawn the subprocesses to avoid counting subprocess module overhead into the benchmark -RUNNER_CODE = ''' -#include -#include -#include -#include - -int main(int argc, char *argv[]) { - if (argc < 3) { - return 1; - } - int n = atoi(argv[1]); - if (n <= 0) { - return 1; - } - char **cmd_argv = &argv[2]; - for (int i = 0; i < n; ++i) { - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - return 1; - } else if (pid == 0) { - execvp(cmd_argv[0], cmd_argv); - perror("execvp"); - exit(127); // If exec fails - } else { - int status; - if (waitpid(pid, &status, 0) < 0) { - perror("waitpid"); - return 1; - } - } - } - return 0; -} -''' - -TMPDIR = tempfile.TemporaryDirectory() -RUNNER_EXE = None -ORIG_ARGV = None - +import fast_subprocess def __setup__(*args): - global RUNNER_EXE - tmpdir = Path(TMPDIR.name) - runner_c = tmpdir / 'runner.c' - runner_c.write_text(RUNNER_CODE) - RUNNER_EXE = tmpdir / 'runner' - subprocess.check_call([os.environ.get('CC', 'gcc'), runner_c, '-O2', '-o', RUNNER_EXE]) - - global ORIG_ARGV - ORIG_ARGV = sys.orig_argv - for i, arg in enumerate(ORIG_ARGV): - if arg.endswith('.py'): - ORIG_ARGV = ORIG_ARGV[:i] - break - try: - ORIG_ARGV.remove('-snapshot-startup') - except ValueError: - pass + fast_subprocess.setup() def __teardown__(): - TMPDIR.cleanup() + fast_subprocess.teardown() def __benchmark__(num=1000000): - subprocess.check_call([ - str(RUNNER_EXE), - str(num), - *ORIG_ARGV, - "-I", # isolate from environment - "-S", # do not import site - "-B", # do not attempt to write pyc files - "-u", # do not add buffering wrappers around output streams - "-c", - "1" - ]) + fast_subprocess.run(num, "1") diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/OWNERS.toml b/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/OWNERS.toml new file mode 100644 index 0000000000..34cb11f338 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/OWNERS.toml @@ -0,0 +1,6 @@ +[[rule]] +files = "*" +any = [ + "francois.farquet@oracle.com", + "andrija.kolic@oracle.com", +] diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/complex_stdlib_import.py b/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/complex_stdlib_import.py new file mode 100644 index 0000000000..11e79a3a40 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/complex_stdlib_import.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# 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. + +""" +Benchmark a more complex standard library import. +""" + + +def run(): + import urllib.request + + +# Warmup benchmarks, by definition, are just a single iteration that we measure end-to-end. +# Thus, the following values are fixed and are not tunable. +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/matplotlib_import.py b/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/matplotlib_import.py new file mode 100644 index 0000000000..8c540150fe --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/matplotlib_import.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# 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. + +""" +Benchmark a more complex third-party import. +""" + + +def run(): + import matplotlib + print(f"Imported {matplotlib.__name__} version '{matplotlib.__version__}'") + + +# Warmup benchmarks, by definition, are just a single iteration that we measure end-to-end. +# Thus, the following values are fixed and are not tunable. +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/simple_stdlib_import.py b/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/simple_stdlib_import.py new file mode 100644 index 0000000000..9189f8e023 --- /dev/null +++ b/graalpython/com.oracle.graal.python.benchmarks/python/warmup/import/simple_stdlib_import.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# 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. + +""" +Benchmark a simple standard library import. +""" + + +def run(): + import email.utils + + +# Warmup benchmarks, by definition, are just a single iteration that we measure end-to-end. +# Thus, the following values are fixed and are not tunable. +def warmupIterations(): + return 0 + + +def iterations(): + return 1 + + +def summary(): + return { + "name": "OutlierRemovalAverageSummary", + "lower-threshold": 0.0, + "upper-threshold": 1.0, + } diff --git a/graalpython/com.oracle.graal.python.bouncycastle/pom.xml b/graalpython/com.oracle.graal.python.bouncycastle/pom.xml new file mode 100644 index 0000000000..5db5641801 --- /dev/null +++ b/graalpython/com.oracle.graal.python.bouncycastle/pom.xml @@ -0,0 +1,79 @@ + + + + 4.0.0 + + + org.graalvm.python.ide + graalpy-source-workspace + 1.0-SNAPSHOT + ../../pom.xml + + + com.oracle.graal.python.bouncycastle + jar + + + + ${project.groupId} + com.oracle.graal.python + + + org.graalvm.sdk + nativeimage + + + org.bouncycastle + bcprov-jdk18on + + + org.bouncycastle + bcpkix-jdk18on + + + org.bouncycastle + bcutil-jdk18on + + + diff --git a/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/native-image/org.graalvm.python/python-bouncycastle/native-image.properties b/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/native-image/org.graalvm.python/python-bouncycastle/native-image.properties new file mode 100644 index 0000000000..13a0ad90d1 --- /dev/null +++ b/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/native-image/org.graalvm.python/python-bouncycastle/native-image.properties @@ -0,0 +1,2 @@ +# Additional native-image arguments for optional GraalPy BouncyCastle support +Args = --features=com.oracle.graal.python.bouncycastle.BouncyCastleFeature diff --git a/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/native-image/org.graalvm.python/python-bouncycastle/reflect-config.json b/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/native-image/org.graalvm.python/python-bouncycastle/reflect-config.json new file mode 100644 index 0000000000..fb104aeb8d --- /dev/null +++ b/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/native-image/org.graalvm.python/python-bouncycastle/reflect-config.json @@ -0,0 +1,2066 @@ +[ + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.CompositeSignatures$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.DSTU4145$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.Dilithium$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.ECGOST$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.EdEC$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.ElGamal$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.Falcon$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.GM$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.GOST$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.IES$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.LMS$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.SPHINCSPlus$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi$ECDSA", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.ec.SignatureSpi$ecDSA256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.dsa.KeyFactorySpi", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Blake2b160", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Blake2b256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Blake2b384", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Blake2b512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Blake2s128", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Blake2s160", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Blake2s224", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Blake2s256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake3$Blake3_256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Blake3$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.DSTU7564$Digest256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.DSTU7564$Digest384", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.DSTU7564$Digest512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.DSTU7564$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.GOST3411$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.GOST3411$Digest2012_256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.GOST3411$Digest2012_512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Haraka$Digest256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Haraka$Digest512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Haraka$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest224", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest288", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest384", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.MD2$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.MD4$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.MD4$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.MD5$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD128$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD128$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD160$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD160$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD256$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD256$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD320$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD320$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA1$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA224$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA256$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestParallelHash128_256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestParallelHash256_512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestShake128_256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestShake256_512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestTupleHash128_256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestTupleHash256_512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA3$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA384$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SHA512$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SM3$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.SM3$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_1024_1024", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_1024_384", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_1024_512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_256_128", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_256_160", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_256_224", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_256_256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_128", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_160", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_224", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_384", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_512", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Skein$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Tiger$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Tiger$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Whirlpool$Digest", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.digest.Whirlpool$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.keystore.BC$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.keystore.BCFKS$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.AES$AlgParams", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.AES$ECB", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.AES$CBC", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.AES$CBC128", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.AES$CBC192", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.AES$CBC256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.AES$PBEWithMD5And256BitAESCBCOpenSSL", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.AES$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.ARIA$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.CAST5$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.CAST6$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Camellia$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.ChaCha$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.DES$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.DSTU7624$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.GOST28147$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.GOST3412_2015$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Grain128$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Grainv1$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.HC128$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Noekeon$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.OpenSSLPBKDF$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF1$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2withSHA256", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Poly1305$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.RC5$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.RC6$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Rijndael$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.SCRYPT$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.SEED$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.SM4$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Salsa20$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Serpent$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Shacal2$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.SipHash$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.SipHash128$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Skipjack$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.TEA$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.TLSKDF$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.VMPC$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.VMPCKSA3$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.XSalsa20$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.XTEA$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.BIKE$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.CMCE$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.Dilithium$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.Falcon$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.Frodo$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.HQC$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.Kyber$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.LMS$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.NH$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.NTRU$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.NTRUPrime$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.Picnic$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.Rainbow$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.SABER$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.SPHINCS$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.SPHINCSPlus$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + }, + { + "name": "org.bouncycastle.pqc.jcajce.provider.XMSS$Mappings", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ], + "condition": { + "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" + } + } +] diff --git a/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/services/com.oracle.graal.python.runtime.crypto.BouncyCastleSupport b/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/services/com.oracle.graal.python.runtime.crypto.BouncyCastleSupport new file mode 100644 index 0000000000..7bfca52a3e --- /dev/null +++ b/graalpython/com.oracle.graal.python.bouncycastle/src/META-INF/services/com.oracle.graal.python.runtime.crypto.BouncyCastleSupport @@ -0,0 +1 @@ +com.oracle.graal.python.bouncycastle.BouncyCastleSupportImpl diff --git a/graalpython/com.oracle.graal.python.bouncycastle/src/com/oracle/graal/python/bouncycastle/BouncyCastleFeature.java b/graalpython/com.oracle.graal.python.bouncycastle/src/com/oracle/graal/python/bouncycastle/BouncyCastleFeature.java new file mode 100644 index 0000000000..eeba6d3295 --- /dev/null +++ b/graalpython/com.oracle.graal.python.bouncycastle/src/com/oracle/graal/python/bouncycastle/BouncyCastleFeature.java @@ -0,0 +1,59 @@ +/* + * 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 + * + * 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.bouncycastle; + +import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; + +public final class BouncyCastleFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + RuntimeClassInitialization.initializeAtBuildTime("org.bouncycastle"); + RuntimeClassInitialization.initializeAtRunTime("org.bouncycastle.jcajce.provider.drbg.DRBG$Default"); + RuntimeClassInitialization.initializeAtRunTime("org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV"); + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } +} diff --git a/graalpython/com.oracle.graal.python.bouncycastle/src/com/oracle/graal/python/bouncycastle/BouncyCastleSupportImpl.java b/graalpython/com.oracle.graal.python.bouncycastle/src/com/oracle/graal/python/bouncycastle/BouncyCastleSupportImpl.java new file mode 100644 index 0000000000..3c5ad556fa --- /dev/null +++ b/graalpython/com.oracle.graal.python.bouncycastle/src/com/oracle/graal/python/bouncycastle/BouncyCastleSupportImpl.java @@ -0,0 +1,122 @@ +/* + * 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.bouncycastle; + +import java.io.IOException; +import java.io.StringReader; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.Security; +import java.security.Signature; + +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; +import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; +import org.bouncycastle.pkcs.PKCSException; + +import com.oracle.graal.python.builtins.objects.ssl.CertUtils.NeedsPasswordException; +import com.oracle.graal.python.runtime.crypto.BouncyCastleSupport; + +public final class BouncyCastleSupportImpl implements BouncyCastleSupport { + private static Provider getProvider() { + Provider provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); + if (provider != null) { + return provider; + } + return new BouncyCastleProvider(); + } + + @Override + public PrivateKey loadPrivateKey(char[] password, String pemText) throws IOException, NeedsPasswordException, GeneralSecurityException { + Provider provider = getProvider(); + try (PEMParser pemParser = new PEMParser(new StringReader(pemText))) { + JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(provider); + Object object; + while ((object = pemParser.readObject()) != null) { + PrivateKeyInfo pkInfo; + if (object instanceof PEMKeyPair) { + pkInfo = ((PEMKeyPair) object).getPrivateKeyInfo(); + } else if (object instanceof PEMEncryptedKeyPair) { + if (password == null) { + throw new NeedsPasswordException(); + } + JcePEMDecryptorProviderBuilder decryptor = new JcePEMDecryptorProviderBuilder().setProvider(provider); + PEMKeyPair keyPair = ((PEMEncryptedKeyPair) object).decryptKeyPair(decryptor.build(password)); + pkInfo = keyPair.getPrivateKeyInfo(); + } else if (object instanceof PKCS8EncryptedPrivateKeyInfo) { + if (password == null) { + throw new NeedsPasswordException(); + } + JceOpenSSLPKCS8DecryptorProviderBuilder decryptor = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider(provider); + pkInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decryptor.build(password)); + } else if (object instanceof PrivateKeyInfo) { + pkInfo = (PrivateKeyInfo) object; + } else { + continue; + } + return converter.getPrivateKey(pkInfo); + } + return null; + } catch (OperatorCreationException | PKCSException e) { + throw new GeneralSecurityException(e); + } + } + + @Override + public MessageDigest createDigest(String algorithm) throws NoSuchAlgorithmException { + return MessageDigest.getInstance(algorithm, getProvider()); + } + + @Override + public Signature createSignature(String algorithm) throws NoSuchAlgorithmException { + return Signature.getInstance(algorithm, getProvider()); + } +} diff --git a/graalpython/com.oracle.graal.python.cext/AGENTS.md b/graalpython/com.oracle.graal.python.cext/AGENTS.md new file mode 100644 index 0000000000..2fec21bb92 --- /dev/null +++ b/graalpython/com.oracle.graal.python.cext/AGENTS.md @@ -0,0 +1,20 @@ +# com.oracle.graal.python.cext/ — CPYTHON C-API COMPAT + +## OVERVIEW +C headers and native code implementing CPython C-API compatibility plus adapted builtin extension modules. + +## WHERE TO LOOK +| Task | Location | Notes | +|------|----------|-------| +| Public C-API headers | `include/` | Mirrors CPython header structure. | +| Core C-API impl | `src/` | CPython-like file naming (`unicodeobject.c`, `typeobject.c`, ...). | +| Adapted extension modules | `modules/` | Large upstream-derived modules (e.g., `_sqlite`). | +| Embedded/third-party imports | `expat/`, `modules/_sqlite/sqlite/` | Treat as upstream; patch minimally. | + +## CONVENTIONS / GOTCHAS +- Follow CPython invariants in headers (examples: do not nest `Py_BEGIN_ALLOW_THREADS`; “never mix allocators” warnings). +- Large unicode databases/headers are often generated (e.g., `unicodename_db.h`, `unicodedata_db.h`): avoid hand edits. + +## ANTI-PATTERNS +- **NEVER** nest `Py_BEGIN_ALLOW_THREADS` blocks (see `include/ceval.h`). +- Never mix `PyMem_*` / `PyObject_*` allocators with platform `malloc` family. diff --git a/graalpython/com.oracle.graal.python.cext/CMakeLists.txt b/graalpython/com.oracle.graal.python.cext/CMakeLists.txt index 8a26d9d95e..ffb606b6ce 100644 --- a/graalpython/com.oracle.graal.python.cext/CMakeLists.txt +++ b/graalpython/com.oracle.graal.python.cext/CMakeLists.txt @@ -50,7 +50,6 @@ endif() require_var(GRAALPY_PARENT_DIR) require_var(CAPI_INC_DIR) require_var(PYCONFIG_INCLUDE_DIR) -require_var(TRUFFLE_NFI_H_INC) require_var(GRAALPY_EXT) if(NOT DEFINED SRC_DIR) @@ -162,7 +161,7 @@ set(SRC_FILES ${CAPI_SRC}/codecs.c ${CAPI_SRC}/setobject.c ${CAPI_SRC}/compile.c ${CAPI_SRC}/fileobject.c ${CAPI_SRC}/pystrcmp.c ${CAPI_SRC}/getversion.c ${CAPI_SRC}/genobject.c ${CAPI_SRC}/methodobject.c ${CAPI_SRC}/boolobject.c ${CAPI_SRC}/pylifecycle.c ${CAPI_SRC}/errors.c ${CAPI_SRC}/signals.c ${CAPI_SRC}/datetime.c ${CAPI_SRC}/call.c - ${CAPI_SRC}/getargs.c ${CAPI_SRC}/tracemalloc.c ${CAPI_SRC}/initconfig.c + ${CAPI_SRC}/getargs.c ${CAPI_SRC}/tracemalloc.c ${CAPI_SRC}/initconfig.c ${CAPI_SRC}/graalpy_stacktrace.c ) file(GLOB_RECURSE ACTUAL_SRC_FILES @@ -200,7 +199,6 @@ include_directories( "${SRC_DIR}/include" "${CAPI_INC_DIR}" "${PYCONFIG_INCLUDE_DIR}" - "${TRUFFLE_NFI_H_INC}" ) function(native_module name core src_files) @@ -370,6 +368,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" @@ -378,10 +379,10 @@ set(CTYPES_SRC "${SRC_DIR}/modules/_ctypes/ctypes.h" "${SRC_DIR}/modules/_ctypes/stgdict.c" ) -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" @@ -428,7 +429,9 @@ elseif(APPLE) HAVE_DECL_RTLD_LOCAL HAVE_DECL_RTLD_GLOBAL HAVE_DYLD_SHARED_CACHE_CONTAINS_PATH - USING_MALLOC_CLOSURE_DOT_C + HAVE_FFI_CLOSURE_ALLOC + HAVE_FFI_PREP_CIF_VAR + HAVE_FFI_PREP_CLOSURE_LOC ) else() target_compile_definitions("_ctypes" PRIVATE @@ -454,6 +457,7 @@ if(WIN32) if (NOT MSVC) target_compile_options(${TARGET_LIBPYTHON} PRIVATE "-fmsc-version=1920") endif() + target_link_libraries(${TARGET_LIBPYTHON} dbghelp) else() # Link to math library; required for functions like 'hypot' or similar target_link_libraries(${TARGET_LIBPYTHON} m) 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/Python.h b/graalpython/com.oracle.graal.python.cext/include/Python.h index a653aa8ea2..00e286ff9e 100644 --- a/graalpython/com.oracle.graal.python.cext/include/Python.h +++ b/graalpython/com.oracle.graal.python.cext/include/Python.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2022, 2024, 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 @@ -100,6 +100,7 @@ struct timeval; #include "pythonrun.h" #include "pylifecycle.h" #include "ceval.h" +#include "intrcheck.h" #include "sysmodule.h" #include "osmodule.h" #include "import.h" 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..cd1897bca7 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,18 @@ typedef struct _err_stackitem { } _PyErr_StackItem; +typedef struct { + PyObject **items; + int len; + int capacity; +} GraalPyDeallocState; + +typedef struct { + PyObject *tuple_empty; + PyObject *bytes_empty; + PyObject **bytes_characters; +} GraalPySingletons; + typedef struct _stack_chunk { struct _stack_chunk *previous; size_t size; @@ -259,9 +271,20 @@ struct _ts { */ PyObject **small_ints; + /* GraalPy change: Similar to small_ints, we keep native wrappers for + CPython's internal static-object singletons in the PyThreadState rather + than in PyInterpreterState or _PyRuntimeState. The native wrappers are + context-local handle-space pointers even though the represented managed + objects are immutable. Do not add public API singletons such as Py_None + here; CPython exposes those through their dedicated globals. */ + GraalPySingletons singletons; + /* 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/include/graalpy/handles.h b/graalpython/com.oracle.graal.python.cext/include/graalpy/handles.h index 8d114337e6..21e346e825 100644 --- a/graalpython/com.oracle.graal.python.cext/include/graalpy/handles.h +++ b/graalpython/com.oracle.graal.python.cext/include/graalpy/handles.h @@ -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 @@ -74,7 +74,7 @@ #define points_to_py_int_handle(PTR) (((uint64_t)(uintptr_t)(PTR)) & INTEGER_TAG_BIT) #define points_to_py_float_handle(PTR) (((uint64_t)(uintptr_t)(PTR)) & FLOAT_TAG_BIT) -#define stub_to_pointer(STUB_PTR) ((uintptr_t)(((uint64_t)(uintptr_t)(PTR)) | HANDLE_TAG_BIT)) +#define stub_to_pointer(STUB_PTR) ((uintptr_t)(((uint64_t)(uintptr_t)(STUB_PTR)) | HANDLE_TAG_BIT)) #define int32_to_pointer(INT) ((uintptr_t)((((uint64_t)(uint32_t)(INT) << 3) & _35_BIT_MASK) | HANDLE_TAG_BIT | INTEGER_TAG_BIT)) static inline PyObject* float_to_pointer(float dbl) { uint32_t float_bits; diff --git a/graalpython/com.oracle.graal.python.cext/include/graalpy/testcapi.h b/graalpython/com.oracle.graal.python.cext/include/graalpy/testcapi.h new file mode 100644 index 0000000000..6b3f9b17d9 --- /dev/null +++ b/graalpython/com.oracle.graal.python.cext/include/graalpy/testcapi.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef GRAALPY_TESTCAPI_H +#define GRAALPY_TESTCAPI_H + +#ifndef GRAALPY_ENABLE_TESTING_CAPI +# error "graalpy/testcapi.h is only available if GRAALPY_ENABLE_TESTING_CAPI is defined" +#endif + +#include +#include +#include + +#define GraalPy_Test_CAPI_NAME "__graalpython__._testcapi" + +typedef struct { + int (*ToNative)(void *); + int (*DisableReferenceQueuePolling)(void); + void (*EnableReferenceQueuePolling)(void); + void (*TriggerGC)(size_t); + uintptr_t (*LongLvTag)(const PyLongObject *); + void (*GraalPyPrivate_LogImpl)(int, const char *, va_list); +} GraalPy_Test_CAPI; + +static GraalPy_Test_CAPI *GraalPyTestCAPI = NULL; + +static inline int GraalPyTestCAPI_Import(void) { + GraalPyTestCAPI = (GraalPy_Test_CAPI *) PyCapsule_Import(GraalPy_Test_CAPI_NAME, 0); + return GraalPyTestCAPI == NULL ? -1 : 0; +} + +#endif diff --git a/graalpython/com.oracle.graal.python.cext/include/internal/pycore_global_objects.h b/graalpython/com.oracle.graal.python.cext/include/internal/pycore_global_objects.h index 65832418e9..2c4ed78c32 100644 --- a/graalpython/com.oracle.graal.python.cext/include/internal/pycore_global_objects.h +++ b/graalpython/com.oracle.graal.python.cext/include/internal/pycore_global_objects.h @@ -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-2023 Python Software Foundation * * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -35,8 +35,11 @@ extern "C" { #define _Py_GLOBAL_OBJECT(NAME) \ _PyRuntime.static_objects.NAME +/* GraalPy change: CPython exposes static singleton storage through _PyRuntime. + GraalPy's native wrappers are context-local handle-space pointers, so keep + them in the PyThreadState next to small_ints. */ #define _Py_SINGLETON(NAME) \ - _Py_GLOBAL_OBJECT(singletons.NAME) + (*(PyThreadState_Get()->singletons.NAME)) #if 0 // GraalPy change struct _Py_cached_objects { diff --git a/graalpython/com.oracle.graal.python.cext/include/internal/pycore_long.h b/graalpython/com.oracle.graal.python.cext/include/internal/pycore_long.h index be822f8f77..97f2defa88 100644 --- a/graalpython/com.oracle.graal.python.cext/include/internal/pycore_long.h +++ b/graalpython/com.oracle.graal.python.cext/include/internal/pycore_long.h @@ -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 @@ -126,7 +126,7 @@ PyAPI_FUNC(char*) _PyLong_FormatBytesWriter( #define SIGN_NEGATIVE 2 #define NON_SIZE_BITS 3 -PyAPI_FUNC(uintptr_t) GraalPyPrivate_Long_lv_tag(const PyLongObject *op); +Py_LOCAL_SYMBOL uintptr_t GraalPyPrivate_Long_lv_tag(const PyLongObject *op); /* The functions _PyLong_IsCompact and _PyLong_CompactValue are defined * in Include/cpython/longobject.h, since they need to be inline. * diff --git a/graalpython/com.oracle.graal.python.cext/include/internal/pycore_object.h b/graalpython/com.oracle.graal.python.cext/include/internal/pycore_object.h index 9d621a15ad..c2251550a0 100644 --- a/graalpython/com.oracle.graal.python.cext/include/internal/pycore_object.h +++ b/graalpython/com.oracle.graal.python.cext/include/internal/pycore_object.h @@ -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 @@ -377,7 +377,7 @@ _PyType_PreHeaderSize(PyTypeObject *tp) void _PyObject_GC_Link(PyObject *op); -PyAPI_FUNC(void) _GraalPyObject_GC_NotifyOwnershipTransfer(PyObject *op); +Py_LOCAL_SYMBOL void _GraalPyObject_GC_NotifyOwnershipTransfer(PyObject *op); // Usage: assert(_Py_CheckSlotResult(obj, "__getitem__", result != NULL)); extern int _Py_CheckSlotResult( diff --git a/graalpython/com.oracle.graal.python.cext/include/intrcheck.h b/graalpython/com.oracle.graal.python.cext/include/intrcheck.h new file mode 100644 index 0000000000..2c940799b2 --- /dev/null +++ b/graalpython/com.oracle.graal.python.cext/include/intrcheck.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2026, Oracle and/or its affiliates. + * Copyright (C) 1996-2020 Python Software Foundation + * + * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 + */ + +#ifndef Py_INTRCHECK_H +#define Py_INTRCHECK_H +#ifdef __cplusplus +extern "C" { +#endif + +PyAPI_FUNC(int) PyOS_InterruptOccurred(void); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 +PyAPI_FUNC(void) PyOS_BeforeFork(void); +PyAPI_FUNC(void) PyOS_AfterFork_Parent(void); +PyAPI_FUNC(void) PyOS_AfterFork_Child(void); +#endif +/* Deprecated, please use PyOS_AfterFork_Child() instead */ +Py_DEPRECATED(3.7) PyAPI_FUNC(void) PyOS_AfterFork(void); + +#ifndef Py_LIMITED_API +PyAPI_FUNC(int) _PyOS_IsMainThread(void); + +#ifdef MS_WINDOWS +/* windows.h is not included by Python.h so use void* instead of HANDLE */ +PyAPI_FUNC(void*) _PyOS_SigintEvent(void); +#endif +#endif /* !Py_LIMITED_API */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTRCHECK_H */ diff --git a/graalpython/com.oracle.graal.python.cext/modules/_cpython_sre/sre.c b/graalpython/com.oracle.graal.python.cext/modules/_cpython_sre/sre.c index e91eee1a3e..d9aeca3d67 100644 --- a/graalpython/com.oracle.graal.python.cext/modules/_cpython_sre/sre.c +++ b/graalpython/com.oracle.graal.python.cext/modules/_cpython_sre/sre.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 @@ -45,7 +45,6 @@ static const char copyright[] = #define PY_SSIZE_T_CLEAN -#include "capi.h" // GraalPy change #include "Python.h" #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_moduleobject.h" // _PyModule_GetState() @@ -3051,14 +3050,19 @@ expand_template(TemplateObject *self, MatchObject *match) #if 0 // GraalPy change result = _PyUnicode_JoinArray(&_Py_STR(empty), out, count); #else // GraalPy change: different implementation - count = GraalPyPrivate_List_TryGetItems(list, &out); - result = _PyUnicode_JoinArray(PyUnicode_FromString(""), out, count); + count = PyList_GET_SIZE(list); + out = GraalPyList_ITEMS(list); + PyObject *empty = PyUnicode_FromString(""); + if (empty == NULL) { + goto cleanup; + } + result = _PyUnicode_JoinArray(empty, out, count); + Py_DECREF(empty); #endif // GraalPy change } else { // Py_SET_SIZE(list, count); // GraalPy change - // GraalPy change: replace '&_Py_SINGLETON(bytes_empty)' with 'GraalPyPrivate_Bytes_EmptyWithCapacity(0)' - result = _PyBytes_Join(GraalPyPrivate_Bytes_EmptyWithCapacity(0), list); + result = _PyBytes_Join((PyObject *)&_Py_SINGLETON(bytes_empty), list); } cleanup: 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..c7aaa5b41b 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 @@ -1453,8 +1453,6 @@ PyIndex_Check(PyObject *obj) return _PyIndex_Check(obj); } - -#if 0 // GraalPy change /* Return a Python int from the object item. Can return an instance of int subclass. Raise TypeError if the result is not an int @@ -1470,6 +1468,12 @@ _PyNumber_Index(PyObject *item) if (PyLong_Check(item)) { return Py_NewRef(item); } + + // GraalPy change: upcall for managed objects + if (points_to_py_handle_space(item)) { + return GraalPyPrivate_PyNumber_Index(item); + } + if (!_PyIndex_Check(item)) { PyErr_Format(PyExc_TypeError, "'%.200s' object cannot be interpreted " @@ -1511,11 +1515,14 @@ PyNumber_Index(PyObject *item) { PyObject *result = _PyNumber_Index(item); if (result != NULL && !PyLong_CheckExact(result)) { - Py_SETREF(result, _PyLong_Copy((PyLongObject *)result)); + if (points_to_py_handle_space(result)) { + Py_SETREF(result, GraalPyPrivate_PyNumber_IndexCopy(result)); + } else { + Py_SETREF(result, _PyLong_Copy((PyLongObject *)result)); + } } return result; } -#endif // GraalPy change /* Return an error on Overflow only if err is not NULL*/ @@ -1571,14 +1578,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 +1595,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 +1699,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 +1713,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 +1773,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) { @@ -2857,10 +2887,15 @@ _PyObject_RealIsSubclass(PyObject *derived, PyObject *cls) return recursive_issubclass(derived, cls); } - +#endif // GraalPy change PyObject * PyObject_GetIter(PyObject *o) { + // GraalPy change: upcall for managed objects + if (points_to_py_handle_space(o)) { + return GraalPyPrivate_Object_GetIter(o); + } + PyTypeObject *t = Py_TYPE(o); getiterfunc f; @@ -2882,7 +2917,7 @@ PyObject_GetIter(PyObject *o) return res; } } - +#if 0 // GraalPy change PyObject * PyObject_GetAIter(PyObject *o) { PyTypeObject *t = Py_TYPE(o); @@ -2901,7 +2936,7 @@ PyObject_GetAIter(PyObject *o) { } return it; } - +#endif // GraalPy change int PyIter_Check(PyObject *obj) { @@ -2909,7 +2944,7 @@ PyIter_Check(PyObject *obj) return (tp->tp_iternext != NULL && tp->tp_iternext != &_PyObject_NextNotImplemented); } - +#if 0 // GraalPy change int PyAIter_Check(PyObject *obj) { diff --git a/graalpython/com.oracle.graal.python.cext/src/bytearrayobject.c b/graalpython/com.oracle.graal.python.cext/src/bytearrayobject.c index b989a18841..e23eeb0c94 100644 --- a/graalpython/com.oracle.graal.python.cext/src/bytearrayobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/bytearrayobject.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 @@ -51,7 +51,7 @@ _getbytevalue(PyObject* arg, int *value) } #endif // GraalPy change -int // GraalPy change: not static +Py_LOCAL_SYMBOL int // GraalPy change: hidden for capi.c bytearray_getbuffer(PyByteArrayObject *obj, Py_buffer *view, int flags) { void *ptr; @@ -68,7 +68,7 @@ bytearray_getbuffer(PyByteArrayObject *obj, Py_buffer *view, int flags) return 0; } -void // GraalPy change: not static +Py_LOCAL_SYMBOL void // GraalPy change: hidden for capi.c bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view) { // GraalPy change diff --git a/graalpython/com.oracle.graal.python.cext/src/bytesobject.c b/graalpython/com.oracle.graal.python.cext/src/bytesobject.c index cb989fad50..dade5bdc54 100644 --- a/graalpython/com.oracle.graal.python.cext/src/bytesobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/bytesobject.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 @@ -9,6 +9,7 @@ #include "capi.h" // GraalPy change #include "Python.h" +#include "pycore_global_objects.h" // _Py_SINGLETON() // GraalPy change #if 0 // GraalPy change #include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_bytesobject.h" // _PyBytes_Find(), _PyBytes_Repeat() @@ -25,6 +26,29 @@ #include +// Return a strong reference to the empty bytes string singleton. +static inline PyObject* bytes_new_empty(void) +{ + return Py_NewRef(&_Py_SINGLETON(bytes_empty)); +} + +// Return a strong reference to a cached one-byte bytes string singleton. +static inline PyObject* bytes_new_character(unsigned char ch) +{ + return Py_NewRef(PyThreadState_Get()->singletons.bytes_characters[ch]); +} + +static inline int bytes_is_character_singleton(PyObject *op) +{ + PyObject **characters = PyThreadState_Get()->singletons.bytes_characters; + for (int i = 0; i < 256; i++) { + if (op == characters[i]) { + return 1; + } + } + return 0; +} + /*[clinic input] class bytes "PyBytesObject *" "&PyBytes_Type" [clinic start generated code]*/ @@ -133,6 +157,12 @@ PyBytes_FromStringAndSize(const char *str, Py_ssize_t size) "Negative size passed to PyBytes_FromStringAndSize"); return NULL; } + if (size == 0) { + return bytes_new_empty(); + } + if (size == 1 && str != NULL) { + return bytes_new_character((unsigned char)*str); + } if (str != NULL) { return GraalPyPrivate_Bytes_FromStringAndSize(str, size); } @@ -143,10 +173,15 @@ PyObject * PyBytes_FromString(const char *str) { // GraalPy change: different implementation - if (str != NULL) { - return GraalPyPrivate_Bytes_FromStringAndSize(str, strlen(str)); - } - return GraalPyPrivate_Bytes_EmptyWithCapacity(0); + assert(str != NULL); + Py_ssize_t size = strlen(str); + if (size == 0) { + return bytes_new_empty(); + } + if (size == 1) { + return bytes_new_character((unsigned char)*str); + } + return GraalPyPrivate_Bytes_FromStringAndSize(str, size); } PyObject * @@ -1587,7 +1622,7 @@ bytes_subscript(PyBytesObject* self, PyObject* item) } #endif // GraalPy change -int // GraalPy change: not static +Py_LOCAL_SYMBOL int // GraalPy change: hidden for capi.c bytes_buffer_getbuffer(PyBytesObject *self, Py_buffer *view, int flags) { // GraalPy change: avoid direct struct access @@ -2820,8 +2855,8 @@ _Py_COMP_DIAG_POP } #endif // GraalPy change -// GraalPy change: export for downcall, rename, use C array instead of bytes -PyAPI_FUNC(PyObject *) +// GraalPy change: helper-table entry for downcall, rename, use C array instead of bytes +GraalPy_CAPI_HELPER_SYMBOL PyObject * GraalPyPrivate_Bytes_SubtypeNew(PyTypeObject *type, int8_t* contents, Py_ssize_t n) { // GraalPy change: different implementation PyObject* bytes = type->tp_alloc(type, n); @@ -2927,6 +2962,32 @@ int _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize) { // GraalPy change: different implementation + PyObject *v = *pv; + if (!PyBytes_Check(v) || newsize < 0) { + *pv = NULL; + Py_DECREF(v); + PyErr_BadInternalCall(); + return -1; + } + if (Py_SIZE(v) == newsize) { + return 0; + } + if (Py_SIZE(v) == 0) { + *pv = GraalPyPrivate_Bytes_EmptyWithCapacity(newsize); + Py_DECREF(v); + return *pv == NULL ? -1 : 0; + } + if (bytes_is_character_singleton(v)) { + *pv = NULL; + Py_DECREF(v); + PyErr_BadInternalCall(); + return -1; + } + if (newsize == 0) { + *pv = bytes_new_empty(); + Py_DECREF(v); + return 0; + } return GraalPyPrivate_Bytes_Resize(*pv, newsize); } @@ -3393,4 +3454,3 @@ _PyBytes_Repeat(char* dest, Py_ssize_t len_dest, } } #endif // GraalPy change - 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 a7517cd427..e90f21d29d 100644 --- a/graalpython/com.oracle.graal.python.cext/src/capi.c +++ b/graalpython/com.oracle.graal.python.cext/src/capi.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,10 +41,23 @@ #include "capi.h" #include #include -#include #include "pycore_gc.h" // _PyGC_InitState +#define GRAALPY_ENABLE_TESTING_CAPI +#include "graalpy/testcapi.h" +#undef GRAALPY_ENABLE_TESTING_CAPI + +Py_LOCAL_SYMBOL void +GraalPyPrivate_LogImpl(int level, const char *format, va_list args) +{ + char buffer[1024]; + int written = PyOS_vsnprintf(buffer, sizeof(buffer), format, args); + if (written >= 0) { + GraalPyPrivate_LogString(level, buffer); + } +} + typedef struct arrayobject { PyObject_VAR_HEAD char *ob_item; @@ -98,7 +111,45 @@ typedef struct { } PyPickleBufferObject; // defined in 'unicodeobject.c' -void unicode_dealloc(PyObject *unicode); +Py_LOCAL_SYMBOL void unicode_dealloc(PyObject *unicode); + +Py_LOCAL_SYMBOL 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"); + } +} + +Py_LOCAL_SYMBOL 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; +} + +Py_LOCAL_SYMBOL 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); @@ -117,7 +168,6 @@ static void capsule_dealloc(PyObject *o) { PyAPI_DATA(PyTypeObject) _PyExc_BaseException; PyAPI_DATA(PyTypeObject) _PyExc_StopIteration; -PyAPI_DATA(PyTypeObject) mmap_object_type; // used for sizeof(...) typedef struct { @@ -142,8 +192,8 @@ typedef struct { int strict; } zipobject; -#define PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, __ITEMSIZE__, __ALLOC__, __DEALLOC__, __FREE__, __VCALL_OFFSET__) \ -PyTypeObject GLOBAL_NAME = {\ +#define PY_TRUFFLE_TYPE_GENERIC_WITH_STORAGE(STORAGE, GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, __ITEMSIZE__, __ALLOC__, __DEALLOC__, __FREE__, __VCALL_OFFSET__) \ +STORAGE PyTypeObject GLOBAL_NAME = {\ PyVarObject_HEAD_INIT((__SUPER_TYPE__), 0)\ __TYPE_NAME__, /* tp_name */\ (__SIZE__), /* tp_basicsize */\ @@ -186,18 +236,27 @@ PyTypeObject GLOBAL_NAME = {\ 0, /* tp_is_gc */\ }; +#define PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, __ITEMSIZE__, __ALLOC__, __DEALLOC__, __FREE__, __VCALL_OFFSET__) \ +PY_TRUFFLE_TYPE_GENERIC_WITH_STORAGE(, GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, __ITEMSIZE__, __ALLOC__, __DEALLOC__, __FREE__, __VCALL_OFFSET__) + +#undef PY_TRUFFLE_TYPE_LOCAL +#define PY_TRUFFLE_TYPE_LOCAL(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__) \ +PY_TRUFFLE_TYPE_GENERIC_WITH_STORAGE(static, GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, 0, 0, 0, 0, 0) + #define PY_TRUFFLE_TYPE_EXTERN(GLOBAL_NAME, NAME) #define PY_TRUFFLE_TYPE_UNIMPLEMENTED(GLOBAL_NAME) PyTypeObject GLOBAL_NAME; PY_TYPE_OBJECTS #undef PY_TRUFFLE_TYPE_GENERIC +#undef PY_TRUFFLE_TYPE_GENERIC_WITH_STORAGE +#undef PY_TRUFFLE_TYPE_LOCAL #undef PY_TRUFFLE_TYPE_EXTERN #undef PY_TRUFFLE_TYPE_UNIMPLEMENTED -#define PUBLIC_BUILTIN(NAME, RET, ...) RET (*GraalPyPrivate_Upcall_##NAME)(__VA_ARGS__); -#define PRIVATE_BUILTIN(NAME, RET, ...) RET (*NAME)(__VA_ARGS__); +#define PUBLIC_BUILTIN(NAME, RET, ...) Py_LOCAL_SYMBOL RET (*GraalPyPrivate_Upcall_##NAME)(__VA_ARGS__); +#define PRIVATE_BUILTIN(NAME, RET, ...) Py_LOCAL_SYMBOL RET (*NAME)(__VA_ARGS__); CAPI_BUILTINS #undef PUBLIC_BUILTIN #undef PRIVATE_BUILTIN @@ -211,18 +270,20 @@ CAPI_BUILTINS #undef PRIVATE_BUILTIN } -uint32_t Py_Truffle_Options; +Py_LOCAL_SYMBOL uint32_t Py_Truffle_Options; #undef bool static void initialize_builtin_types_and_structs() { clock_t t = clock(); - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINE, "initialize_builtin_types_and_structs..."); + GraalPyPrivate_Log(GRAALPY_LOG_FINE, "initialize_builtin_types_and_structs..."); static int64_t builtin_types[] = { #define PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, a, b, c, d, e, f, g) &GLOBAL_NAME, __TYPE_NAME__, +#define PY_TRUFFLE_TYPE_LOCAL(GLOBAL_NAME, __TYPE_NAME__, a, b) &GLOBAL_NAME, __TYPE_NAME__, #define PY_TRUFFLE_TYPE_EXTERN(GLOBAL_NAME, __TYPE_NAME__) &GLOBAL_NAME, __TYPE_NAME__, #define PY_TRUFFLE_TYPE_UNIMPLEMENTED(GLOBAL_NAME) // empty PY_TYPE_OBJECTS #undef PY_TRUFFLE_TYPE_GENERIC +#undef PY_TRUFFLE_TYPE_LOCAL #undef PY_TRUFFLE_TYPE_EXTERN #undef PY_TRUFFLE_TYPE_UNIMPLEMENTED NULL, NULL @@ -233,20 +294,19 @@ static void initialize_builtin_types_and_structs() { // fix up for circular dependency: PyType_Type.tp_base = &PyBaseObject_Type; - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINE, "initialize_builtin_types_and_structs: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC); + GraalPyPrivate_Log(GRAALPY_LOG_FINE, "initialize_builtin_types_and_structs: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC); } -int mmap_getbuffer(PyObject *self, Py_buffer *view, int flags) { - // TODO(fa) readonly flag +static int mmap_getbuffer(PyObject *self, Py_buffer *view, int flags) { char* data = GraalPyPrivate_GetMMapData(self); if (!data) { return -1; } - return PyBuffer_FillInfo(view, (PyObject*)self, data, PyObject_Size((PyObject *)self), 0, flags); + return PyBuffer_FillInfo(view, (PyObject*)self, data, PyObject_Size((PyObject *)self), GraalPyPrivate_MMap_IsReadonly(self), flags); } -PyAPI_FUNC(void) GraalPyPrivate_MMap_InitBufferProtocol(PyObject* mmap_type) { - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINE, "GraalPyPrivate_MMap_InitBufferProtocol"); +GraalPy_CAPI_HELPER_SYMBOL void GraalPyPrivate_MMap_InitBufferProtocol(PyObject* mmap_type) { + GraalPyPrivate_Log(GRAALPY_LOG_FINE, "GraalPyPrivate_MMap_InitBufferProtocol"); assert(PyType_Check(mmap_type)); static PyBufferProcs mmap_as_buffer = { @@ -272,26 +332,32 @@ PyObject* _Py_NotImplementedStructReference; */ THREAD_LOCAL PyThreadState *tstate_current = NULL; -static void initialize_globals() { - // store the thread state into a thread local variable - tstate_current = GraalPyPrivate_ThreadState_Get(&tstate_current); +GraalPy_CAPI_HELPER_SYMBOL PyThreadState **GraalPyPrivate_InitThreadStateCurrent(PyThreadState *tstate) { + tstate_current = tstate; + graalpy_initialize_thread_state_singletons(tstate); + return &tstate_current; +} + +static void initialize_globals(PyThreadState *tstate) { + GraalPyPrivate_InitThreadStateCurrent(tstate); _Py_NoneStructReference = GraalPyPrivate_None(); _Py_NotImplementedStructReference = GraalPyPrivate_NotImplemented(); _Py_EllipsisObjectReference = GraalPyPrivate_Ellipsis(); _Py_TrueStructReference = (struct _longobject*)GraalPyPrivate_True(); _Py_FalseStructReference = (struct _longobject*)GraalPyPrivate_False(); + graalpy_initialize_thread_state_singletons(tstate); } /* internal functions to avoid unnecessary managed <-> native conversions */ /* BYTES, BYTEARRAY */ -int bytes_buffer_getbuffer(PyBytesObject *self, Py_buffer *view, int flags); -int bytearray_getbuffer(PyByteArrayObject *obj, Py_buffer *view, int flags); -void bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view); +Py_LOCAL_SYMBOL int bytes_buffer_getbuffer(PyBytesObject *self, Py_buffer *view, int flags); +Py_LOCAL_SYMBOL int bytearray_getbuffer(PyByteArrayObject *obj, Py_buffer *view, int flags); +Py_LOCAL_SYMBOL void bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view); /* MEMORYVIEW */ -int memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags); -void memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view); +Py_LOCAL_SYMBOL int memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags); +Py_LOCAL_SYMBOL void memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view); /* PICKLEBUFFER */ static int @@ -349,67 +415,23 @@ static void initialize_gc_types_related_slots() { int is_builtin_type(PyTypeObject *tp) { #define PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, a, b, c, d, e, f, g) (tp == &GLOBAL_NAME) || +#define PY_TRUFFLE_TYPE_LOCAL(GLOBAL_NAME, __TYPE_NAME__, a, b) (tp == &GLOBAL_NAME) || #define PY_TRUFFLE_TYPE_EXTERN(GLOBAL_NAME, __TYPE_NAME__) PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, 0, 0, 0, 0, 0, 0, 0) #define PY_TRUFFLE_TYPE_UNIMPLEMENTED(GLOBAL_NAME) // empty return PY_TYPE_OBJECTS 0; #undef PY_TRUFFLE_TYPE_GENERIC +#undef PY_TRUFFLE_TYPE_LOCAL #undef PY_TRUFFLE_TYPE_EXTERN #undef PY_TRUFFLE_TYPE_UNIMPLEMENTED } -/** to be used from Java code only; calls INCREF */ -PyAPI_FUNC(void) GraalPyPrivate_INCREF(PyObject* obj) { - Py_INCREF(obj); -} - -/** to be used from Java code only; calls DECREF */ -PyAPI_FUNC(void) GraalPyPrivate_DECREF(PyObject* obj) { - Py_DECREF(obj); -} - /** to be used from Java code only; calls '_Py_Dealloc' */ -PyAPI_FUNC(Py_ssize_t) -GraalPyPrivate_SUBREF(intptr_t ptr, Py_ssize_t value) -{ - PyObject *obj = (PyObject*)ptr; // avoid type attachment at the interop boundary -#ifndef NDEBUG - if (obj->ob_refcnt & 0xFFFFFFFF00000000L) { - char buf[1024]; - sprintf(buf, - "suspicious refcnt value during managed adjustment for %p (%zd 0x%zx - %zd)\n", - obj, obj->ob_refcnt, obj->ob_refcnt, value); - Py_FatalError(buf); - } - if ((obj->ob_refcnt - value) < 0) { - char buf[1024]; - sprintf(buf, - "refcnt below zero during managed adjustment for %p (%zd 0x%zx - %zd)\n", - obj, obj->ob_refcnt, obj->ob_refcnt, value); - Py_FatalError(buf); - } -#endif // NDEBUG - - Py_ssize_t new_value = ((obj->ob_refcnt) -= value); - if (new_value == 0) { - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINER, "%s: _Py_Dealloc(0x%zx)", - __func__, obj); - _Py_Dealloc(obj); - } -#ifdef Py_REF_DEBUG - else if (new_value < 0) { - _Py_NegativeRefcount(filename, lineno, op); - } -#endif - return new_value; -} - -/** to be used from Java code only; calls '_Py_Dealloc' */ -PyAPI_FUNC(Py_ssize_t) +GraalPy_CAPI_HELPER_SYMBOL Py_ssize_t GraalPyPrivate_BulkDealloc(intptr_t ptrArray[], int64_t len) { for (int i = 0; i < len; i++) { PyObject *obj = (PyObject *)ptrArray[i]; - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINER, + GraalPyPrivate_Log(GRAALPY_LOG_FINER, "%s: _Py_Dealloc(a %s at 0x%zx)", __func__, Py_TYPE(obj)->tp_name, obj); _Py_Dealloc(obj); @@ -418,7 +440,7 @@ GraalPyPrivate_BulkDealloc(intptr_t ptrArray[], int64_t len) } /** to be used from Java code only and only at exit; calls _Py_Dealloc */ -PyAPI_FUNC(Py_ssize_t) +GraalPy_CAPI_HELPER_SYMBOL Py_ssize_t GraalPyPrivate_BulkDeallocOnShutdown(intptr_t ptrArray[], int64_t len) { /* some objects depends on others which might get deallocated in the @@ -434,7 +456,7 @@ GraalPyPrivate_BulkDeallocOnShutdown(intptr_t ptrArray[], int64_t len) /* we don't need to care about objects with default deallocation process */ obj->ob_refcnt = 0; - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINER, "%s: _Py_Dealloc(0x%zx)", + GraalPyPrivate_Log(GRAALPY_LOG_FINER, "%s: _Py_Dealloc(0x%zx)", __func__, obj); _Py_Dealloc(obj); } @@ -445,11 +467,11 @@ GraalPyPrivate_BulkDeallocOnShutdown(intptr_t ptrArray[], int64_t len) /* To be used from Java code only. * This function is used if a native class inherits from a managed class but uses the 'object.__new__'. * This function roughly corresponds to CPython's 'object_new'. */ -PyAPI_FUNC(PyObject*) GraalPyPrivate_ObjectNew(PyTypeObject* cls) { +GraalPy_CAPI_HELPER_SYMBOL PyObject* GraalPyPrivate_ObjectNew(PyTypeObject* cls) { return cls->tp_alloc(cls, 0); } -PyAPI_FUNC(void) GraalPyPrivate_ObjectArrayRelease(PyObject** array, int32_t size) { +GraalPy_CAPI_HELPER_SYMBOL void GraalPyPrivate_ObjectArrayRelease(PyObject** array, int32_t size) { for (int i = 0; i < size; i++) { /* This needs to use 'Py_XDECREF' because we use this function to deallocate storages of tuples, lists, ..., where this is done in the @@ -467,7 +489,7 @@ PyAPI_FUNC(void) GraalPyPrivate_ObjectArrayRelease(PyObject** array, int32_t siz #include "psapi.h" #endif -PyAPI_FUNC(size_t) GraalPyPrivate_GetCurrentRSS() { +GraalPy_CAPI_HELPER_SYMBOL size_t GraalPyPrivate_GetCurrentRSS(void) { size_t rss = 0; #if defined(__APPLE__) && defined(__MACH__) // MacOS @@ -498,110 +520,8 @@ 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) { +GraalPy_CAPI_HELPER_SYMBOL int GraalPyPrivate_CheckBasicsizeForGetstate(PyTypeObject* type, int slot_num) { Py_ssize_t basicsize = PyBaseObject_Type.tp_basicsize; if (type->tp_dictoffset) basicsize += sizeof(PyObject *); @@ -612,52 +532,34 @@ PyAPI_FUNC(int) GraalPyPrivate_CheckBasicsizeForGetstate(PyTypeObject* type, int return type->tp_basicsize > basicsize; } -PyAPI_FUNC(void) GraalPyPrivate_CheckTypeReady(PyTypeObject* type) { +GraalPy_CAPI_HELPER_SYMBOL void GraalPyPrivate_CheckTypeReady(PyTypeObject* type) { if (!(type->tp_flags & Py_TPFLAGS_READY)) { PyType_Ready(type); } } -PyAPI_FUNC(void*) GraalPyPrivate_VaArgPointer(va_list* va) { - return va_arg(*va, void*); -} - -PyAPI_FUNC(int) GraalPyPrivate_NoOpClear(PyObject* o) { +GraalPy_CAPI_HELPER_SYMBOL int GraalPyPrivate_NoOpClear(PyObject* o) { return 0; } -PyAPI_FUNC(int) GraalPyPrivate_NoOpTraverse(PyObject *self, visitproc visit, void *arg) { +GraalPy_CAPI_HELPER_SYMBOL int GraalPyPrivate_NoOpTraverse(PyObject *self, visitproc visit, void *arg) { return 0; } // defined in 'exceptions.c' -void initialize_exceptions(); +Py_LOCAL_SYMBOL void initialize_exceptions(); // defined in 'pyhash.c' -void initialize_hashes(); +Py_LOCAL_SYMBOL void initialize_hashes(); // defined in 'floatobject.c' void _PyFloat_InitState(PyInterpreterState* state); /* - * This is used to allow Truffle to enter/leave the context on native threads - * that were not created by NFI/Truffle/Java and thus not previously attached - * to the context. See e.g. PyGILState_Ensure. This is used by some C - * extensions to allow calling Python APIs from natively created threads. This - * poses a problem if multiple contexts use the same library, since we cannot - * know which context should be entered. CPython has the same problem (see - * https://docs.python.org/3/c-api/init.html#bugs-and-caveats), in particular - * the following quote: - * - * Furthermore, extensions (such as ctypes) using these APIs to allow calling - * of Python code from non-Python created threads will probably be broken - * when using sub-interpreters. - * - * If we try to use the same libpython for multiple contexts, we can only - * behave in a similar (likely broken) way as CPython: natively created threads - * that use the PyGIL_* APIs to allow calling into Python will attach to the - * first interpreter that initialized the C API (and thus set the - * TRUFFLE_CONTEXT pointer) only. + * These functions allow Truffle to enter/leave the context on native threads + * that were not created by Truffle/Java and thus do not have a previously + * entered polyglot context. See e.g. PyGILState_Ensure. */ -Py_LOCAL_SYMBOL TruffleContext* TRUFFLE_CONTEXT; +Py_LOCAL_SYMBOL graalpy_attach_native_thread_func graalpy_attach_native_thread = NULL; +Py_LOCAL_SYMBOL graalpy_detach_native_thread_func graalpy_detach_native_thread = NULL; /* * This is only set during VM shutdown, so on the native side can only be used @@ -667,14 +569,14 @@ Py_LOCAL_SYMBOL TruffleContext* TRUFFLE_CONTEXT; */ Py_LOCAL_SYMBOL int8_t *_graalpy_finalizing = NULL; -PyAPI_FUNC(void) initialize_graal_capi(TruffleEnv* env, void **builtin_closures, GCState *gc) { +PyAPI_FUNC(PyThreadState **) initialize_graal_capi(void **builtin_closures, GCState *gc, + PyThreadState *tstate, graalpy_attach_native_thread_func attach_native_thread, + graalpy_detach_native_thread_func detach_native_thread) { clock_t t = clock(); - if (env) { - TRUFFLE_CONTEXT = (*env)->getTruffleContext(env); - } - _PyGC_InitState(gc); + graalpy_attach_native_thread = attach_native_thread; + graalpy_detach_native_thread = detach_native_thread; /* * Initializing all these global fields with pointers to different contexts @@ -701,12 +603,12 @@ PyAPI_FUNC(void) initialize_graal_capi(TruffleEnv* env, void **builtin_closures, * context exits and its table is the "latest", we delay freeing it. */ initialize_builtins(builtin_closures); - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINE, "initialize_builtins: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC); + GraalPyPrivate_Log(GRAALPY_LOG_FINE, "initialize_builtins: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC); Py_Truffle_Options = GraalPyPrivate_Native_Options(); initialize_builtin_types_and_structs(); // initialize global variables like '_Py_NoneStruct', etc. - initialize_globals(); + initialize_globals(tstate); initialize_exceptions(); initialize_hashes(); initialize_bufferprocs(); @@ -716,14 +618,15 @@ PyAPI_FUNC(void) initialize_graal_capi(TruffleEnv* env, void **builtin_closures, // TODO: initialize during cext initialization doesn't work at the moment Py_FileSystemDefaultEncoding = "utf-8"; // strdup(PyUnicode_AsUTF8(GraalPyPrivate_FileSystemDefaultEncoding())); - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINE, "initialize_graal_capi: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC); + GraalPyPrivate_Log(GRAALPY_LOG_FINE, "initialize_graal_capi: %fs", ((double) (clock() - t)) / CLOCKS_PER_SEC); + return &tstate_current; } /* * This function is called from Java during C API initialization to get the * pointer `_graalpy_finalizing`. */ -PyAPI_FUNC(int8_t *) GraalPyPrivate_GetFinalizeCApiPointer() { +GraalPy_CAPI_HELPER_SYMBOL void *GraalPyPrivate_GetFinalizeCApiPointer(void) { assert(!_graalpy_finalizing); // We actually leak this memory on purpose. On the Java side, this is // written to in a VM shutdown hook. Once such a hook is registered it diff --git a/graalpython/com.oracle.graal.python.cext/src/capi.h b/graalpython/com.oracle.graal.python.cext/src/capi.h index 4323916fdb..cbaad1205e 100644 --- a/graalpython/com.oracle.graal.python.cext/src/capi.h +++ b/graalpython/com.oracle.graal.python.cext/src/capi.h @@ -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 @@ -65,6 +65,12 @@ #error "don't know how to declare thread local variable" #endif +typedef int (*graalpy_attach_native_thread_func)(void); +typedef void (*graalpy_detach_native_thread_func)(void); + +extern graalpy_attach_native_thread_func graalpy_attach_native_thread; +extern graalpy_detach_native_thread_func graalpy_detach_native_thread; + #ifdef MS_WINDOWS // define the below, otherwise windows' sdk defines complex to _complex and breaks us #define _COMPLEX_DEFINED @@ -87,16 +93,6 @@ #define SRC_CS "utf-8" -/* Flags definitions representing global (debug) options. */ -#define PY_TRUFFLE_TRACE_MEM 0x1 -#define PY_TRUFFLE_LOG_INFO 0x2 -#define PY_TRUFFLE_LOG_CONFIG 0x4 -#define PY_TRUFFLE_LOG_FINE 0x8 -#define PY_TRUFFLE_LOG_FINER 0x10 -#define PY_TRUFFLE_LOG_FINEST 0x20 -#define PY_TRUFFLE_DEBUG_CAPI 0x40 -#define PY_TRUFFLE_PYTHON_GC 0x80 - typedef struct mmap_object mmap_object; typedef struct _gc_runtime_state GCState; // originally in 'gcmodule.c' @@ -127,7 +123,7 @@ typedef struct { PyGC_Head *reachable; } GraalPyGC_Cycle; -PyAPI_FUNC(void) GraalPyPrivate_DebugTrace(void); +Py_LOCAL_SYMBOL void GraalPyPrivate_DebugTrace(void); typedef struct { PyObject_HEAD @@ -145,40 +141,71 @@ typedef struct { double ob_fval; } GraalPyFloatObject; +typedef struct { + GraalPyObject ob_base; + Py_ssize_t length; + Py_ssize_t byte_length; + Py_hash_t hash; + /* Bits 0-2: kind; bit 3: is_ascii; bits 4-5: interned state. */ + uint64_t state; + void *data; +} GraalPyUnicodeObject; + typedef struct gc_generation GCGeneration; +#define GraalPy_CAPI_HELPER_SYMBOL Py_LOCAL_SYMBOL + // {{start CAPI_BUILTINS}} #include "capi.gen.h" -#define PUBLIC_BUILTIN(NAME, RET, ...) extern PyAPI_FUNC(RET) (*GraalPyPrivate_Upcall_##NAME)(__VA_ARGS__); -#define PRIVATE_BUILTIN(NAME, RET, ...) extern PyAPI_FUNC(RET) (*NAME)(__VA_ARGS__); +#define PUBLIC_BUILTIN(NAME, RET, ...) extern Py_LOCAL_SYMBOL RET (*GraalPyPrivate_Upcall_##NAME)(__VA_ARGS__); +#define PRIVATE_BUILTIN(NAME, RET, ...) extern Py_LOCAL_SYMBOL RET (*NAME)(__VA_ARGS__); CAPI_BUILTINS #undef PUBLIC_BUILTIN #undef PRIVATE_BUILTIN #define GET_SLOT_SPECIAL(OBJ, RECEIVER, NAME, SPECIAL) ( points_to_py_handle_space(OBJ) ? GraalPyPrivate_Get_##RECEIVER##_##NAME((RECEIVER*) (OBJ)) : ((RECEIVER*) (OBJ))->SPECIAL ) -PyAPI_DATA(uint32_t) Py_Truffle_Options; +extern Py_LOCAL_SYMBOL uint32_t Py_Truffle_Options; extern THREAD_LOCAL Py_LOCAL_SYMBOL PyThreadState *tstate_current; +static inline void graalpy_initialize_thread_state_singletons(PyThreadState *tstate) { + if (tstate == NULL || GraalPyPrivate_Tuple_Empty == NULL || GraalPyPrivate_Bytes_Empty == NULL || GraalPyPrivate_Bytes_FromStringAndSize == NULL) { + return; + } + if (tstate->singletons.tuple_empty == NULL) { + tstate->singletons.tuple_empty = GraalPyPrivate_Tuple_Empty(); + } + if (tstate->singletons.bytes_empty == NULL) { + tstate->singletons.bytes_empty = GraalPyPrivate_Bytes_Empty(); + } + if (tstate->singletons.bytes_characters != NULL && tstate->singletons.bytes_characters[0] == NULL) { + for (int i = 0; i < 256; i++) { + char ch = (char)i; + tstate->singletons.bytes_characters[i] = GraalPyPrivate_Bytes_FromStringAndSize(&ch, 1); + if (tstate->singletons.bytes_characters[i] == NULL) { + Py_FatalError("failed to initialize GraalPy one-byte bytes singleton"); + } + } + } + if (tstate->singletons.tuple_empty == NULL || tstate->singletons.bytes_empty == NULL || tstate->singletons.bytes_characters == NULL || tstate->singletons.bytes_characters[0] == NULL) { + Py_FatalError("failed to initialize GraalPy thread-state singletons"); + } +} + extern Py_LOCAL_SYMBOL int8_t *_graalpy_finalizing; #define graalpy_finalizing (_graalpy_finalizing != NULL && *_graalpy_finalizing) +Py_LOCAL_SYMBOL void graalpy_dealloc_stack_grow(PyThreadState *tstate); +Py_LOCAL_SYMBOL void graalpy_dealloc_stack_push(PyThreadState *tstate, PyObject *op); +Py_LOCAL_SYMBOL void graalpy_dealloc_stack_pop(PyThreadState *tstate, PyObject *op); + #if (__linux__ && __GNU_LIBRARY__) #include #include -#include #include -static void print_c_stacktrace() { - fprintf(stderr, "Native stacktrace:\n"); - intptr_t stack[16]; - size_t stack_size = backtrace((void *)stack, sizeof(stack) / sizeof(stack[0])); - backtrace_symbols_fd((void *)stack, stack_size, STDERR_FILENO); - fflush(stderr); -} - static void attach_gdb() { pid_t my_pid = getpid(); char* pathname = "/bin/sh"; @@ -197,132 +224,67 @@ static void attach_gdb() { } } #else -static void print_c_stacktrace() { - // not supported -} - static void attach_gdb() { // not supported } #endif +Py_LOCAL_SYMBOL size_t GraalPyPrivate_CaptureStacktrace(void **frames, size_t max_depth, size_t skip); +Py_LOCAL_SYMBOL void GraalPyPrivate_PrintCapturedStacktrace(FILE *file, const char *header, void *const *frames, size_t depth); +Py_LOCAL_SYMBOL void GraalPyPrivate_PrintCurrentStacktrace(FILE *file, const char *header, size_t max_depth, size_t skip); +Py_LOCAL_SYMBOL void GraalPyPrivate_LogCapturedStacktrace(int level, const char *prefix, void *const *frames, size_t depth); +Py_LOCAL_SYMBOL void GraalPyPrivate_LogImpl(int level, const char *format, va_list args); + +static inline void print_c_stacktrace() { + GraalPyPrivate_PrintCurrentStacktrace(stderr, "Native stacktrace:\n", 16, 0); +} + /* Flags definitions representing global (debug) options. */ static MUST_INLINE int GraalPyPrivate_Trace_Memory() { - return Py_Truffle_Options & PY_TRUFFLE_TRACE_MEM; + return Py_Truffle_Options & GRAALPY_TRACE_MEM; } static MUST_INLINE int GraalPyPrivate_Log_Info() { - return Py_Truffle_Options & PY_TRUFFLE_LOG_INFO; + return Py_Truffle_Options & GRAALPY_LOG_INFO; } static MUST_INLINE int GraalPyPrivate_Log_Config() { - return Py_Truffle_Options & PY_TRUFFLE_LOG_CONFIG; + return Py_Truffle_Options & GRAALPY_LOG_CONFIG; } static MUST_INLINE int GraalPyPrivate_Log_Fine() { - return Py_Truffle_Options & PY_TRUFFLE_LOG_FINE; + return Py_Truffle_Options & GRAALPY_LOG_FINE; } static MUST_INLINE int GraalPyPrivate_Log_Finer() { - return Py_Truffle_Options & PY_TRUFFLE_LOG_FINER; + return Py_Truffle_Options & GRAALPY_LOG_FINER; } static MUST_INLINE int GraalPyPrivate_Log_Finest() { - return Py_Truffle_Options & PY_TRUFFLE_LOG_FINEST; + return Py_Truffle_Options & GRAALPY_LOG_FINEST; } static MUST_INLINE int GraalPyPrivate_Debug_CAPI() { - return Py_Truffle_Options & PY_TRUFFLE_DEBUG_CAPI; + return Py_Truffle_Options & GRAALPY_DEBUG_CAPI; } static MUST_INLINE int GraalPyPrivate_PythonGC() { - return Py_Truffle_Options & PY_TRUFFLE_PYTHON_GC; + return Py_Truffle_Options & GRAALPY_PYTHON_GC; +} +static MUST_INLINE int GraalPyPrivate_PoisonNativeMemoryOnFree() { + return Py_Truffle_Options & GRAALPY_POISON_NATIVE_MEMORY_ON_FREE; +} +static MUST_INLINE int GraalPyPrivate_SampleNativeMemoryAllocSites() { + return Py_Truffle_Options & GRAALPY_SAMPLE_NATIVE_MEMORY_ALLOC_SITES; } -static void +static inline void GraalPyPrivate_Log(int level, const char *format, ...) { if (Py_Truffle_Options & level) { - char buffer[1024]; va_list args; va_start(args, format); - vsprintf(buffer, format, args); - GraalPyPrivate_LogString(level, buffer); + GraalPyPrivate_LogImpl(level, format, args); va_end(args); } } 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 PyAPI_FUNC(int) _PyArg_Parse_SizeT(PyObject *, const char *, ...); PyAPI_FUNC(int) _PyArg_ParseTuple_SizeT(PyObject *, const char *, ...); @@ -345,14 +307,15 @@ PY_TRUFFLE_TYPE(_PyBytesIOBuffer_Type, "_BytesIOBuffer", & PY_TRUFFLE_TYPE(_PyExc_BaseException, "BaseException", &PyType_Type, sizeof(PyBaseExceptionObject)) \ PY_TRUFFLE_TYPE(_PyExc_Exception, "Exception", &PyType_Type, sizeof(PyBaseExceptionObject)) \ PY_TRUFFLE_TYPE(_PyExc_StopIteration, "StopIteration", &PyType_Type, sizeof(PyStopIterationObject)) \ +PY_TRUFFLE_TYPE(_PyExc_SyntaxError, "SyntaxError", &PyType_Type, sizeof(PySyntaxErrorObject)) \ PY_TRUFFLE_TYPE(_PyNamespace_Type, "SimpleNamespace", &PyType_Type, sizeof(_PyNamespaceObject)) \ PY_TRUFFLE_TYPE(_PyNone_Type, "NoneType", &PyType_Type, 0) \ PY_TRUFFLE_TYPE(_PyNotImplemented_Type, "NotImplementedType", &PyType_Type, 0) \ PY_TRUFFLE_TYPE(_PyWeakref_CallableProxyType, "_weakref.CallableProxyType", &PyType_Type, sizeof(PyWeakReference)) \ PY_TRUFFLE_TYPE(_PyWeakref_ProxyType, "_weakref.ProxyType", &PyType_Type, sizeof(PyWeakReference)) \ PY_TRUFFLE_TYPE(_PyWeakref_RefType, "_weakref.ReferenceType", &PyType_Type, sizeof(PyWeakReference)) \ -PY_TRUFFLE_TYPE(Arraytype, "array", &PyType_Type, sizeof(arrayobject)) \ -PY_TRUFFLE_TYPE(mmap_object_type, "mmap.mmap", &PyType_Type, 0) \ +PY_TRUFFLE_TYPE_LOCAL(Arraytype, "array", &PyType_Type, sizeof(arrayobject)) \ +PY_TRUFFLE_TYPE_LOCAL(mmap_object_type, "mmap.mmap", &PyType_Type, 0) \ PY_TRUFFLE_TYPE(PyArrayIter_Type, "arrayiterator", &PyType_Type, sizeof(arrayiterobject)) \ PY_TRUFFLE_TYPE(PyAsyncGen_Type, "async_generator", &PyType_Type, sizeof(PyAsyncGenObject)) \ PY_TRUFFLE_TYPE_WITH_ITEMSIZE(PyLong_Type, "int", &PyType_Type, offsetof(PyLongObject, long_value.ob_digit), sizeof(PyObject *)) \ @@ -395,7 +358,7 @@ PY_TRUFFLE_TYPE_GENERIC(PyUnicode_Type, "str", & PY_TRUFFLE_TYPE(PyWrapperDescr_Type, "wrapper_descriptor", &PyType_Type, sizeof(PyWrapperDescrObject)) \ PY_TRUFFLE_TYPE(PyZip_Type, "zip", &PyType_Type, sizeof(zipobject)) \ PY_TRUFFLE_TYPE(PyReversed_Type, "reversed", &PyType_Type, sizeof(PyObject)) \ -PY_TRUFFLE_TYPE(cycle_type, "cycle", &PyType_Type, sizeof(PyObject)) \ +PY_TRUFFLE_TYPE_LOCAL(cycle_type, "cycle", &PyType_Type, sizeof(PyObject)) \ PY_TRUFFLE_TYPE(PySeqIter_Type, "iterator", &PyType_Type, sizeof(PyObject)) \ PY_TRUFFLE_TYPE(PyEnum_Type, "enumerate", &PyType_Type, sizeof(PyObject)) \ PY_TRUFFLE_TYPE(PyCoro_Type, "coroutine", &PyType_Type, sizeof(PyCoroObject)) \ @@ -457,6 +420,7 @@ PY_TRUFFLE_TYPE_UNIMPLEMENTED(PyUnicodeIter_Type) \ #define PY_TRUFFLE_TYPE_WITH_ALLOC(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, __ALLOC__, __DEALLOC__, __FREE__) PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, 0, __ALLOC__, __DEALLOC__, __FREE__, 0) #define PY_TRUFFLE_TYPE(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__) PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, 0, 0, 0, 0, 0) +#define PY_TRUFFLE_TYPE_LOCAL(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__) PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, 0, 0, 0, 0, 0) #define PY_TRUFFLE_TYPE_WITH_ITEMSIZE(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, __ITEMSIZE__) PY_TRUFFLE_TYPE_GENERIC(GLOBAL_NAME, __TYPE_NAME__, __SUPER_TYPE__, __SIZE__, __ITEMSIZE__, 0, 0, 0, 0) diff --git a/graalpython/com.oracle.graal.python.cext/src/capsule.c b/graalpython/com.oracle.graal.python.cext/src/capsule.c index 0ad9b8537b..be9b34b835 100644 --- a/graalpython/com.oracle.graal.python.cext/src/capsule.c +++ b/graalpython/com.oracle.graal.python.cext/src/capsule.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2021, 2025, Oracle and/or its affiliates. +/* Copyright (c) 2021, 2026, Oracle and/or its affiliates. * Copyright (C) 1996-2021 Python Software Foundation * * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -10,11 +10,10 @@ #include "capi.h" -PyAPI_FUNC(PyTypeObject*) getPyCapsuleTypeReference() { +Py_LOCAL_SYMBOL PyTypeObject* getPyCapsuleTypeReference() { return &PyCapsule_Type; } -PyAPI_FUNC(void) GraalPyPrivate_Capsule_CallDestructor(PyObject* capsule, PyCapsule_Destructor destructor) { +GraalPy_CAPI_HELPER_SYMBOL void GraalPyPrivate_Capsule_CallDestructor(PyObject* capsule, PyCapsule_Destructor destructor) { destructor(capsule); } - 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/complexobject.c b/graalpython/com.oracle.graal.python.cext/src/complexobject.c index 1659e3510d..956482d44b 100644 --- a/graalpython/com.oracle.graal.python.cext/src/complexobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/complexobject.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 @@ -239,7 +239,7 @@ PyComplex_FromCComplex(Py_complex cval) return PyComplex_FromDoubles(cval.real, cval.imag); } -PyAPI_FUNC(PyObject *) // GraalPy change: expose the function for downcalls, rename +GraalPy_CAPI_HELPER_SYMBOL PyObject * // GraalPy change: helper-table entry for downcalls, rename GraalPyPrivate_Complex_SubtypeFromDoubles(PyTypeObject *type, double real, double imag) { Py_complex c; diff --git a/graalpython/com.oracle.graal.python.cext/src/context.c b/graalpython/com.oracle.graal.python.cext/src/context.c index 729c68a109..d4c2da07d7 100644 --- a/graalpython/com.oracle.graal.python.cext/src/context.c +++ b/graalpython/com.oracle.graal.python.cext/src/context.c @@ -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 @@ -41,7 +41,7 @@ #include "capi.h" -PyObject marker_struct = { +static PyObject marker_struct = { _PyObject_EXTRA_INIT { _Py_IMMORTAL_REFCNT }, &PyBaseObject_Type diff --git a/graalpython/com.oracle.graal.python.cext/src/datetime.c b/graalpython/com.oracle.graal.python.cext/src/datetime.c index a769392a07..318ed40fdd 100644 --- a/graalpython/com.oracle.graal.python.cext/src/datetime.c +++ b/graalpython/com.oracle.graal.python.cext/src/datetime.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 @@ -7263,7 +7263,7 @@ pretty bizarre, and a tzinfo subclass can override fromutc() if it is. // GraalPy additions -long +Py_LOCAL_SYMBOL long GraalPyPrivate_PyDateTime_GET_LONG_FIELD(PyObject* o, const char* field_name) { PyObject* attr = PyObject_GetAttrString(o, field_name); @@ -7286,8 +7286,8 @@ GraalPyPrivate_PyDateTime_GET_LONG_FIELD(PyObject* o, const char* field_name) } /** to be used from Java code only; returns the type ID for a PyDateTime_CAPI */ -PyAPI_FUNC(void) -GraalPyPrivate_InitNativeDateTime() +GraalPy_CAPI_HELPER_SYMBOL void +GraalPyPrivate_InitNativeDateTime(void) { /* safe native get/set descriptors */ PyGetSetDef* getsets_date = PyDateTime_DateType.tp_getset; @@ -7313,7 +7313,7 @@ GraalPyPrivate_InitNativeDateTime() } // Used from Java to initialize native subtypes -PyAPI_FUNC(PyObject*) +GraalPy_CAPI_HELPER_SYMBOL PyObject* GraalPyPrivate_Time_SubtypeNew(PyTypeObject* type, int hour, int minute, int second, int usecond, PyObject* tzinfo, int fold) { char aware = tzinfo != NULL; PyDateTime_Time *self = (PyDateTime_Time *) (type->tp_alloc(type, aware)); @@ -7332,7 +7332,7 @@ GraalPyPrivate_Time_SubtypeNew(PyTypeObject* type, int hour, int minute, int sec return (PyObject *)self; } -PyAPI_FUNC(PyObject*) +GraalPy_CAPI_HELPER_SYMBOL PyObject* GraalPyPrivate_Date_SubtypeNew(PyTypeObject* type, int year, int month, int day) { PyDateTime_Date *self = (PyDateTime_Date *)(type->tp_alloc(type, 0)); if (self) { @@ -7344,7 +7344,7 @@ GraalPyPrivate_Date_SubtypeNew(PyTypeObject* type, int year, int month, int day) return (PyObject *)self; } -PyAPI_FUNC(PyObject*) +GraalPy_CAPI_HELPER_SYMBOL PyObject* GraalPyPrivate_DateTime_SubtypeNew(PyTypeObject* type, int year, int month, int day, int hour, int minute, int second, int usecond, PyObject* tzinfo, int fold) { char aware = tzinfo != NULL; PyDateTime_DateTime *self = (PyDateTime_DateTime *) (type->tp_alloc(type, aware)); @@ -7368,7 +7368,7 @@ GraalPyPrivate_DateTime_SubtypeNew(PyTypeObject* type, int year, int month, int return (PyObject *)self; } -PyAPI_FUNC(PyObject*) +GraalPy_CAPI_HELPER_SYMBOL PyObject* GraalPyPrivate_TimeDelta_SubtypeNew(PyTypeObject* type, int days, int seconds, int microseconds) { PyDateTime_Delta *self = (PyDateTime_Delta *) (type->tp_alloc(type, 0)); if (self != NULL) { 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..14faebd390 100644 --- a/graalpython/com.oracle.graal.python.cext/src/dictobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/dictobject.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 @@ -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) { @@ -3074,7 +3086,7 @@ PyDict_Copy(PyObject *o) Py_DECREF(copy); return NULL; } - +#endif // GraalPy change Py_ssize_t PyDict_Size(PyObject *mp) { @@ -3082,9 +3094,12 @@ PyDict_Size(PyObject *mp) PyErr_BadInternalCall(); return -1; } + if (points_to_py_handle_space(mp)) { + return GraalPyPrivate_Object_Size(mp); + } return ((PyDictObject *)mp)->ma_used; } - +#if 0 // GraalPy change PyObject * PyDict_Keys(PyObject *mp) { @@ -3836,7 +3851,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/exceptions.c b/graalpython/com.oracle.graal.python.cext/src/exceptions.c index 7d64a73f9c..9cab823127 100644 --- a/graalpython/com.oracle.graal.python.cext/src/exceptions.c +++ b/graalpython/com.oracle.graal.python.cext/src/exceptions.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 @@ -46,13 +46,13 @@ PY_EXCEPTIONS #undef EXCEPTION -void initialize_exceptions() { +Py_LOCAL_SYMBOL void initialize_exceptions() { #define EXCEPTION(NAME) PyExc_##NAME = (PyObject*) GraalPyPrivate_Type(#NAME); PY_EXCEPTIONS #undef EXCEPTION } -PyAPI_FUNC(PyObject *) +GraalPy_CAPI_HELPER_SYMBOL PyObject * GraalPyPrivate_Exception_SubtypeNew(PyTypeObject *type, PyObject *args) { PyBaseExceptionObject *self; diff --git a/graalpython/com.oracle.graal.python.cext/src/floatobject.c b/graalpython/com.oracle.graal.python.cext/src/floatobject.c index a620a08f61..ffa5ccf77f 100644 --- a/graalpython/com.oracle.graal.python.cext/src/floatobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/floatobject.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-2017 Python Software Foundation * * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -2684,12 +2684,11 @@ double GraalPyFloat_AS_DOUBLE(PyObject *op) { // not quite as in CPython, this assumes that x is already a double. The rest of // the implementation is in the Float constructor in Java -PyAPI_FUNC(PyObject*) +GraalPy_CAPI_HELPER_SYMBOL PyObject* GraalPyPrivate_Float_SubtypeNew(PyTypeObject *type, double x) { PyObject* newobj = type->tp_alloc(type, 0); if (newobj == NULL) { - Py_DECREF(newobj); return NULL; } ((PyFloatObject *)newobj)->ob_fval = x; diff --git a/graalpython/com.oracle.graal.python.cext/src/gcmodule.c b/graalpython/com.oracle.graal.python.cext/src/gcmodule.c index de313043a1..842f2c59eb 100644 --- a/graalpython/com.oracle.graal.python.cext/src/gcmodule.c +++ b/graalpython/com.oracle.graal.python.cext/src/gcmodule.c @@ -49,19 +49,19 @@ call_traverse(traverseproc traverse, PyObject *op, visitproc visit, void *arg) return 0; } if (!traverse) { - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINE, + GraalPyPrivate_Log(GRAALPY_LOG_FINE, "type '%.100s' is a GC type but tp_traverse is NULL", Py_TYPE((op))->tp_name); return 0; } else { if (_PyObject_IsFreed(op)) { - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINE, + GraalPyPrivate_Log(GRAALPY_LOG_FINE, "we tried to call tp_traverse on a freed object at %p (ctx %p)!", op, arg); return 0; } if (_PyObject_IsFreed((PyObject *)Py_TYPE(op))) { - GraalPyPrivate_Log(PY_TRUFFLE_LOG_FINE, + GraalPyPrivate_Log(GRAALPY_LOG_FINE, "we tried to call tp_traverse on an object at %p with a freed type at %p (ctx %p)!", op, Py_TYPE(op), arg); return 0; @@ -554,6 +554,13 @@ visit_weak_reachable(PyObject *op, PyGC_Head *reachable) return 0; } + /* Untracked objects cannot be weak candidates, even if they are reached + * from a container traversed in this phase. + */ + if (!_PyObject_GC_IS_TRACKED(op)) { + return 0; + } + PyGC_Head *gc = AS_GC(op); /* 'visit_reachable' tests at this point for 'gc_is_collecting(gc)'. @@ -563,10 +570,6 @@ visit_weak_reachable(PyObject *op, PyGC_Head *reachable) * 'NEXT_MASK_UNREACHABLE' is set. */ - // It would be a logic error elsewhere if the collecting flag were set on - // an untracked object. - assert(UNTAG(gc)->_gc_next != 0); - /* Note: one could expect that we need to test * 'gc_get_refs(gc) == MANAGED_REFCNT' but that's no longer true because in * the previous phase (i.e. 'move_unreachable') when we move the object into @@ -891,7 +894,11 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable, // GraalPy change: this branch, else branch is original CPython code cycle.head = NULL; cycle.n = 0; - // TODO: [GR-72218] assert (cycle.reachable == weak_candidates ); + /* GraalPy change: visit_collect_managed_referents forwards to + * visit_reachable(cycle->reachable), and 'cycle' is initialized + * with 'young'. + */ + assert(cycle.reachable == young); /* visit_collect_managed_referents is visit_reachable + capture the references into "cycle" */ CALL_TRAVERSE(traverse, op, visit_collect_managed_referents, (void *)&cycle); @@ -979,12 +986,25 @@ move_weak_reachable(PyGC_Head *young, PyGC_Head *weak_candidates) while (gc != young) { Py_ssize_t gc_refcnt = gc_get_refs(gc); - // TODO: [GR-72218] assert(gc_is_collecting(gc)); - + /* GraalPy change: unlike CPython's single-phase flow, objects moved to + * 'weak_candidates' already had PREV_MASK_COLLECTING cleared in + * move_unreachable() before this phase runs. visit_weak_reachable() + * depends on that state so it can rescue weak candidates without + * moving them through visit_reachable() again, so gc_is_collecting(gc) + * is not a valid invariant here. + */ + // assert(gc_is_collecting(gc)); /* This phase is done after 'move_unreachable' and so all object * remaining in 'young' must have a non-zero gc_refcnt. */ - // TODO: [GR-72218] assert(gc_refcnt); + /* GraalPy change: gc_refcnt is also not a stable CPython-style + * invariant here. During the weak-candidate flow we reuse '_gc_prev' + * for list linkage, so native gc_refs information for rescued objects + * is recovered via update_refs()/subtract_refs() and + * is_referenced_from_managed(), not by requiring gc_refcnt != 0 for + * every object we encounter in this phase. + */ + // assert(gc_refcnt); /* If gc_refcnt s not MANAGED_REFCNT, then we know that this object is * referenced from native (e.g. stored in a global field). In case that * 'gc_refcnt == MANAGED_REFCNT' we don't know if the object is only @@ -1331,6 +1351,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); @@ -1346,6 +1367,7 @@ handle_legacy_finalizers(PyThreadState *tstate, } } } +#endif // GraalPy change gc_list_merge(finalizers, old); } @@ -1404,10 +1426,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; @@ -1623,15 +1647,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) { @@ -1781,7 +1806,7 @@ gc_collect_main(PyThreadState *tstate, int generation, PyDTrace_GC_DONE(n + m); } - GraalPyPrivate_EnableReferneceQueuePolling(); + GraalPyPrivate_EnableReferenceQueuePolling(); assert(!_PyErr_Occurred(tstate)); return n + m; @@ -2918,7 +2943,7 @@ PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) } -void +GraalPy_CAPI_HELPER_SYMBOL void GraalPyPrivate_Object_GC_Del(void *op) { if (is_managed(op)) { @@ -2929,13 +2954,13 @@ GraalPyPrivate_Object_GC_Del(void *op) } /* Exposes 'gc_collect_impl' such that we can call it from Java. */ -PyAPI_FUNC(Py_ssize_t) +GraalPy_CAPI_HELPER_SYMBOL Py_ssize_t GraalPyPrivate_GC_Collect(int generation) { return gc_collect_impl(NULL, generation); } -PyAPI_FUNC(void) +Py_LOCAL_SYMBOL void _GraalPyObject_GC_NotifyOwnershipTransfer(PyObject *op) { if (!is_managed(op) && _PyObject_IS_GC(op) && _PyObject_GC_IS_TRACKED(op)) { diff --git a/graalpython/com.oracle.graal.python.cext/src/graalpy_stacktrace.c b/graalpython/com.oracle.graal.python.cext/src/graalpy_stacktrace.c new file mode 100644 index 0000000000..6e64e982c1 --- /dev/null +++ b/graalpython/com.oracle.graal.python.cext/src/graalpy_stacktrace.c @@ -0,0 +1,278 @@ +/* + * 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. + */ + +#include "capi.h" + +#include +#include +#include +#include + +#if defined(MS_WINDOWS) +#include +#include +#elif (defined(__linux__) && defined(__GNU_LIBRARY__)) || defined(__APPLE__) +#include +#endif + +#define GRAALPY_NATIVE_STACK_MAX_NAME 1024 +#define GRAALPY_NATIVE_STACK_LINE_BUFFER 2048 +typedef void (*GraalPyStacktraceWriter)(void *ctx, const char *line); + +static void +render_unavailable_stacktrace(GraalPyStacktraceWriter writer, void *ctx) +{ + writer(ctx, ""); +} + +#if defined(MS_WINDOWS) + +static int +ensure_windows_symbols_initialized(void) +{ + static int initialized = 0; + if (!initialized) { + HANDLE process = GetCurrentProcess(); + SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); + if (!SymInitialize(process, NULL, TRUE)) { + return 0; + } + initialized = 1; + } + return 1; +} + +static const char * +windows_basename(const char *path) +{ + const char *slash = strrchr(path, '\\'); + const char *alt = strrchr(path, '/'); + const char *base = slash != NULL ? slash + 1 : path; + if (alt != NULL && (slash == NULL || alt > slash)) { + base = alt + 1; + } + return base; +} + +static void +render_windows_stacktrace(GraalPyStacktraceWriter writer, void *ctx, void *const *frames, size_t depth) +{ + HANDLE process = GetCurrentProcess(); + char line[GRAALPY_NATIVE_STACK_LINE_BUFFER]; + char symbol_buffer[sizeof(SYMBOL_INFO) + GRAALPY_NATIVE_STACK_MAX_NAME]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO) symbol_buffer; + + memset(symbol_buffer, 0, sizeof(symbol_buffer)); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = GRAALPY_NATIVE_STACK_MAX_NAME - 1; + + if (!ensure_windows_symbols_initialized()) { + for (size_t i = 0; i < depth; i++) { + snprintf(line, sizeof(line), "frame[%lu]: %p", + (unsigned long) i, (void *) frames[i]); + writer(ctx, line); + } + return; + } + + for (size_t i = 0; i < depth; i++) { + DWORD64 address = (DWORD64) (uintptr_t) frames[i]; + DWORD64 displacement = 0; + IMAGEHLP_LINE64 source_line; + DWORD source_displacement = 0; + char module_path[MAX_PATH] = {'\0'}; + const char *module_name = NULL; + HMODULE module = NULL; + + memset(&source_line, 0, sizeof(source_line)); + source_line.SizeOfStruct = sizeof(source_line); + + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR) frames[i], &module) && GetModuleFileNameA(module, module_path, MAX_PATH) > 0) { + module_name = windows_basename(module_path); + } + + if (SymFromAddr(process, address, &displacement, symbol)) { + if (SymGetLineFromAddr64(process, address, &source_displacement, &source_line)) { + if (module_name != NULL) { + snprintf(line, sizeof(line), "frame[%lu]: %s!%s+0x%llx (%s:%lu) [%p]", + (unsigned long) i, module_name, symbol->Name, (unsigned long long) displacement, + source_line.FileName, (unsigned long) source_line.LineNumber, (void *) frames[i]); + } else { + snprintf(line, sizeof(line), "frame[%lu]: %s+0x%llx (%s:%lu) [%p]", + (unsigned long) i, symbol->Name, (unsigned long long) displacement, + source_line.FileName, (unsigned long) source_line.LineNumber, (void *) frames[i]); + } + } else if (module_name != NULL) { + snprintf(line, sizeof(line), "frame[%lu]: %s!%s+0x%llx [%p]", + (unsigned long) i, module_name, symbol->Name, (unsigned long long) displacement, (void *) frames[i]); + } else { + snprintf(line, sizeof(line), "frame[%lu]: %s+0x%llx [%p]", + (unsigned long) i, symbol->Name, (unsigned long long) displacement, (void *) frames[i]); + } + } else if (module_name != NULL) { + snprintf(line, sizeof(line), "frame[%lu]: %s [%p]", + (unsigned long) i, module_name, (void *) frames[i]); + } else { + snprintf(line, sizeof(line), "frame[%lu]: %p", + (unsigned long) i, (void *) frames[i]); + } + writer(ctx, line); + } +} + +#elif (defined(__linux__) && defined(__GNU_LIBRARY__)) || defined(__APPLE__) + +static void +render_execinfo_stacktrace(GraalPyStacktraceWriter writer, void *ctx, void *const *frames, size_t depth) +{ + char **symbols = backtrace_symbols((void *const *) frames, (int) depth); + char line[GRAALPY_NATIVE_STACK_LINE_BUFFER]; + if (symbols == NULL) { + for (size_t i = 0; i < depth; i++) { + snprintf(line, sizeof(line), "frame[%lu]: %p", + (unsigned long) i, (void *) frames[i]); + writer(ctx, line); + } + return; + } + + for (size_t i = 0; i < depth; i++) { + snprintf(line, sizeof(line), "frame[%lu]: %s", (unsigned long) i, symbols[i]); + writer(ctx, line); + } + free(symbols); +} + +#endif + +Py_LOCAL_SYMBOL size_t +GraalPyPrivate_CaptureStacktrace(void **frames, size_t max_depth, size_t skip) +{ + if (frames == NULL || max_depth == 0) { + return 0; + } +#if defined(MS_WINDOWS) + return (size_t) CaptureStackBackTrace((ULONG) (skip + 1), (ULONG) max_depth, frames, NULL); +#elif (defined(__linux__) && defined(__GNU_LIBRARY__)) || defined(__APPLE__) + int raw_depth = backtrace(frames, (int) (max_depth + skip + 1)); + size_t depth = raw_depth > 0 ? (size_t) raw_depth : 0; + size_t start = depth > (skip + 1) ? (skip + 1) : depth; + size_t usable_depth = depth - start; + if (usable_depth > 0) { + memmove(frames, frames + start, usable_depth * sizeof(void *)); + } + return usable_depth; +#else + return 0; +#endif +} + +static void +render_stacktrace(GraalPyStacktraceWriter writer, void *ctx, void *const *frames, size_t depth) +{ + if (depth == 0) { + render_unavailable_stacktrace(writer, ctx); + return; + } +#if defined(MS_WINDOWS) + render_windows_stacktrace(writer, ctx, frames, depth); +#elif (defined(__linux__) && defined(__GNU_LIBRARY__)) || defined(__APPLE__) + render_execinfo_stacktrace(writer, ctx, frames, depth); +#else + (void) frames; + render_unavailable_stacktrace(writer, ctx); +#endif +} + +static void +file_writer(void *ctx, const char *line) +{ + fprintf((FILE *) ctx, "%s\n", line); +} + +Py_LOCAL_SYMBOL void +GraalPyPrivate_PrintCapturedStacktrace(FILE *file, const char *header, void *const *frames, size_t depth) +{ + if (header != NULL) { + fputs(header, file); + } + render_stacktrace(file_writer, file, frames, depth); + fflush(file); +} + +Py_LOCAL_SYMBOL void +GraalPyPrivate_PrintCurrentStacktrace(FILE *file, const char *header, size_t max_depth, size_t skip) +{ + void *frames[64]; + size_t depth = max_depth; + if (depth > (sizeof(frames) / sizeof(frames[0]))) { + depth = sizeof(frames) / sizeof(frames[0]); + } + depth = GraalPyPrivate_CaptureStacktrace(frames, depth, skip + 1); + GraalPyPrivate_PrintCapturedStacktrace(file, header, frames, depth); +} + +typedef struct { + int level; + const char *prefix; +} LogWriterCtx; + +static void +log_writer(void *ctx, const char *line) +{ + LogWriterCtx *log_ctx = (LogWriterCtx *) ctx; + if (log_ctx->prefix != NULL) { + GraalPyPrivate_Log(log_ctx->level, "%s%s\n", log_ctx->prefix, line); + } else { + GraalPyPrivate_Log(log_ctx->level, "%s\n", line); + } +} + +Py_LOCAL_SYMBOL void +GraalPyPrivate_LogCapturedStacktrace(int level, const char *prefix, void *const *frames, size_t depth) +{ + if ((Py_Truffle_Options & level) == 0) { + return; + } + LogWriterCtx log_ctx = {level, prefix}; + render_stacktrace(log_writer, &log_ctx, frames, depth); +} diff --git a/graalpython/com.oracle.graal.python.cext/src/listobject.c b/graalpython/com.oracle.graal.python.cext/src/listobject.c index 3dcb2e302d..f001263af4 100644 --- a/graalpython/com.oracle.graal.python.cext/src/listobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/listobject.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 @@ -48,7 +48,7 @@ _list_clear(PyListObject *a) /* Because XDECREF can recursively invoke operations on this list, we make it empty first. */ - i = GraalPyPrivate_List_TryGetItems((PyObject *)a, &item); + i = GraalPyPrivate_List_ClearManagedOrGetItems((PyObject *)a, &item); if (i > 0) { assert(item != NULL); while (--i >= 0) { diff --git a/graalpython/com.oracle.graal.python.cext/src/longobject.c b/graalpython/com.oracle.graal.python.cext/src/longobject.c index 8d55014735..7962702ed8 100644 --- a/graalpython/com.oracle.graal.python.cext/src/longobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/longobject.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-2017 Python Software Foundation * * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -335,8 +335,16 @@ PyLong_FromLong(long ival) return PyLong_FromLongLong((long long) ival); } -#if 0 // GraalPy change #define PYLONG_FROM_UINT(INT_TYPE, ival) \ + do { \ + if ((ival) <= INT32_MAX) { \ + return int32_to_pointer((int)(ival)); \ + } \ + return GraalPyPrivate_Long_FromUnsignedLongLong((unsigned long long)(ival)); \ + } while(0) + +#if 0 // GraalPy change +#define PYLONG_FROM_UINT_CPYTHON(INT_TYPE, ival) \ do { \ if (IS_SMALL_UINT(ival)) { \ return get_small_int((sdigit)(ival)); \ @@ -359,6 +367,7 @@ PyLong_FromLong(long ival) } \ return (PyObject *)v; \ } while(0) +#endif // GraalPy change /* Create a new int object from a C unsigned long int */ @@ -404,6 +413,7 @@ PyLong_FromDouble(double dval) return PyLong_FromLong((long)dval); } +#if 0 // GraalPy change PyLongObject *v; double frac; int i, ndig, expo, neg; @@ -439,8 +449,9 @@ PyLong_FromDouble(double dval) _PyLong_FlipSign(v); } return (PyObject *)v; -} #endif // GraalPy change + return GraalPyPrivate_Long_FromDouble(dval); +} /* Checking for overflow in PyLong_AsLong is a PITA since C doesn't define * anything about what happens when a signed integer operation overflows, @@ -615,20 +626,27 @@ PyLong_AsUnsignedLongMask(PyObject *op) return (unsigned long) GraalPyPrivate_Long_AsPrimitive(op, MODE_COERCE_MASK, sizeof(unsigned long)); } -#if 0 // GraalPy change int _PyLong_Sign(PyObject *vv) { + assert(vv != NULL); + if (points_to_py_int_handle(vv)) { + int64_t value = pointer_to_int64(vv); + return (value > 0) - (value < 0); + } + PyLongObject *v = (PyLongObject *)vv; assert(v != NULL); assert(PyLong_Check(v)); +#if 0 // GraalPy change if (_PyLong_IsCompact(v)) { return _PyLong_CompactSign(v); } return _PyLong_NonCompactSign(v); -} #endif // GraalPy change + return 1 - (GraalPyPrivate_Long_lv_tag(v) & SIGN_MASK); +} static int bit_length_digit(digit x) @@ -640,10 +658,19 @@ bit_length_digit(digit x) return _Py_bit_length((unsigned long)x); } -#if 0 // GraalPy change size_t _PyLong_NumBits(PyObject *vv) { + assert(vv != NULL); + if (points_to_py_int_handle(vv)) { + int64_t value = pointer_to_int64(vv); + unsigned long magnitude = value < 0 ? (unsigned long)-value : (unsigned long)value; + return (size_t)_Py_bit_length(magnitude); + } + + return GraalPyPrivate_Long_NumBits(vv); + +#if 0 // GraalPy change PyLongObject *v = (PyLongObject *)vv; size_t result = 0; Py_ssize_t ndigits; @@ -669,8 +696,10 @@ _PyLong_NumBits(PyObject *vv) PyErr_SetString(PyExc_OverflowError, "int has too many bits " "to express in a platform size_t"); return (size_t)-1; +#endif // GraalPy change } +#if 0 // GraalPy change PyObject * _PyLong_FromByteArray(const unsigned char* bytes, size_t n, int little_endian, int is_signed) @@ -929,9 +958,9 @@ PyLong_FromVoidPtr(void *p) return PyLong_FromUnsignedLongLong((uint64_t)p); } -#if 0 // GraalPy change /* Get a C pointer from an int object. */ +#if 0 // GraalPy change void * PyLong_AsVoidPtr(PyObject *vv) { @@ -964,6 +993,16 @@ PyLong_AsVoidPtr(PyObject *vv) return NULL; return (void *)x; } +#else +void * +PyLong_AsVoidPtr(PyObject *vv) +{ + if (points_to_py_int_handle(vv)) { + return (void *)(uintptr_t)pointer_to_int64(vv); + } + + return (void *)GraalPyPrivate_Long_AsVoidPtr(vv); +} #endif // GraalPy change /* Initial long long support by Chris Herborth (chrish@qnx.com), later @@ -2453,6 +2492,7 @@ PyLong_FromString(const char *str, char **pend, int base) { int sign = 1, error_if_nonzero = 0; const char *start, *orig_str = str; + int orig_base = base; PyObject *z = NULL; PyObject *strobj; Py_ssize_t slen; @@ -2517,7 +2557,6 @@ PyLong_FromString(const char *str, char **pend, int base) long long result = strtoll(str, &endptr, base); if (error_if_nonzero && result != 0) { // let upcall handle the error reporting - base = 0; break; } // POSIX.1-2008: strtoll must not set errno on success, and set @@ -2538,11 +2577,53 @@ PyLong_FromString(const char *str, char **pend, int base) } } if (!z) { - z = GraalPyPrivate_Long_FromString((char *)orig_str, base); - if (z) { - // TODO: we should probably set the **pend out argument + z = GraalPyPrivate_Long_FromString(orig_str, orig_base); + if (!z && pend) { + /* + * We have an exception already, but we need to redo the validation + * to compute pend. Adapted from long_from_string_base + */ + *pend = (char *)str; + const char *end, *p; + char prev = 0; + start = p = str; + /* Leading underscore not allowed. */ + if (*start == '_') { + return NULL; + } + /* Verify all characters are digits and underscores. */ + while (_PyLong_DigitValue[Py_CHARMASK(*p)] < base || *p == '_') { + if (*p == '_') { + /* Double underscore not allowed. */ + if (prev == '_') { + *pend = (char *)(p - 1); + return NULL; + } + } + prev = *p; + ++p; + } + /* Trailing underscore not allowed. */ + if (prev == '_') { + *pend = (char *)(p - 1); + return NULL; + } + end = p; + *pend = (char *)end; + /* Reject empty strings */ + if (start == end) { + return NULL; + } + /* Allow only trailing whitespace after `end` */ + while (*p && Py_ISSPACE(*p)) { + p++; + } + *pend = (char *)p; } } + if (z && pend) { + *pend = (char *)(str + strlen(str)); + } return z; } @@ -6136,6 +6217,13 @@ PyUnstable_Long_CompactValue(const PyLongObject* op) { #endif // GraalPy change +#undef PyUnstable_Long_IsCompact + +int +PyUnstable_Long_IsCompact(const PyLongObject* op) { + return GraalPyPrivate_Long_lv_tag(op) < (2 << NON_SIZE_BITS); +} + #undef PyUnstable_Long_CompactValue Py_ssize_t PyUnstable_Long_CompactValue(const PyLongObject *op) { @@ -6146,7 +6234,7 @@ Py_ssize_t PyUnstable_Long_CompactValue(const PyLongObject *op) { } // GraalPy additions -uintptr_t GraalPyPrivate_Long_lv_tag(const PyLongObject *op) { +Py_LOCAL_SYMBOL uintptr_t GraalPyPrivate_Long_lv_tag(const PyLongObject *op) { if (points_to_py_int_handle(op)) { int64_t t = pointer_to_int64(op); if (t == 0) { diff --git a/graalpython/com.oracle.graal.python.cext/src/memoryobject.c b/graalpython/com.oracle.graal.python.cext/src/memoryobject.c index 296b6e4102..84e514a441 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 @@ -1503,7 +1503,7 @@ memoryview_toreadonly_impl(PyMemoryViewObject *self) /* getbuffer */ /**************************************************************************/ -int // GraalPy change: not static +Py_LOCAL_SYMBOL int // GraalPy change: hidden for capi.c memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags) { Py_buffer *base = &self->view; @@ -1582,7 +1582,7 @@ memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags) return 0; } -void // GraalPy change: not static +Py_LOCAL_SYMBOL void // GraalPy change: hidden for capi.c memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view) { self->exports--; @@ -3407,19 +3407,18 @@ 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 *) +GraalPy_CAPI_HELPER_SYMBOL PyObject * GraalPyPrivate_MemoryViewFromObject(PyObject *v, int flags) { if (PyObject_CheckBuffer(v)) { Py_buffer* buffer = malloc(sizeof(Py_buffer)); + if (buffer == NULL) { + PyErr_NoMemory(); + return NULL; + } if (PyObject_GetBuffer(v, buffer, flags) < 0) { + free(buffer); return NULL; } int needs_release = 0; @@ -3442,6 +3441,11 @@ GraalPyPrivate_MemoryViewFromObject(PyObject *v, int flags) buffer->shape, buffer->strides, buffer->suboffsets); + if (mv == NULL) { + PyBuffer_Release(buffer); + free(buffer); + return NULL; + } if (!needs_release) { free(buffer); } @@ -3455,7 +3459,7 @@ GraalPyPrivate_MemoryViewFromObject(PyObject *v, int flags) } /* Release buffer struct allocated in GraalPyPrivate_MemoryViewFromObject */ -PyAPI_FUNC(void) +GraalPy_CAPI_HELPER_SYMBOL void GraalPyPrivate_ReleaseBuffer(Py_buffer* buffer) { if (buffer->obj != NULL) { 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..230a49414e 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 @@ -1776,7 +1776,6 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) } -#if 0 // GraalPy change /* Test a value used as condition, e.g., in a while or if statement. Return -1 if an error occurred */ @@ -1790,6 +1789,9 @@ PyObject_IsTrue(PyObject *v) return 0; if (v == Py_None) return 0; + // GraalPy change: upcall for managed objects + if (points_to_py_handle_space(v)) + return GraalPyPrivate_Object_IsTrue(v); else if (Py_TYPE(v)->tp_as_number != NULL && Py_TYPE(v)->tp_as_number->nb_bool != NULL) res = (*Py_TYPE(v)->tp_as_number->nb_bool)(v); @@ -1804,7 +1806,6 @@ PyObject_IsTrue(PyObject *v) /* if it is negative, it should be either -1 or -2 */ return (res > 0) ? 1 : Py_SAFE_DOWNCAST(res, Py_ssize_t, int); } -#endif // GraalPy change /* equivalent of 'not v' Return -1 if an error occurred */ @@ -1819,7 +1820,6 @@ PyObject_Not(PyObject *v) return res == 0; } -#if 0 // GraalPy change /* Test whether an object can be called */ int @@ -1830,6 +1830,7 @@ PyCallable_Check(PyObject *x) return Py_TYPE(x)->tp_call != NULL; } +#if 0 // GraalPy change /* Helper for PyObject_Dir without arguments: returns the local scope. */ static PyObject * @@ -2741,14 +2742,18 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, Py_FatalError("_PyObject_AssertFailed"); } - void _Py_Dealloc(PyObject *op) { + if (points_to_py_handle_space(op) && _PyObject_IS_GC(op)) { + GraalPyPrivate_ManagedObject_GC_Del(op); + return; + } + + 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 +2765,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 +2866,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"); } @@ -2960,7 +2971,7 @@ void GraalPy_SET_TYPE(PyObject *a, PyTypeObject *b) { if (points_to_py_handle_space(a)) { - GraalPyPrivate_Log(PY_TRUFFLE_LOG_INFO, + GraalPyPrivate_Log(GRAALPY_LOG_INFO, "changing the type of an object is not supported\n"); } else { a->ob_type = b; @@ -2998,8 +3009,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/obmalloc.c b/graalpython/com.oracle.graal.python.cext/src/obmalloc.c index ae39e1adac..6683b778df 100644 --- a/graalpython/com.oracle.graal.python.cext/src/obmalloc.c +++ b/graalpython/com.oracle.graal.python.cext/src/obmalloc.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 @@ -50,12 +50,28 @@ typedef struct { size_t dummy; } mem_head_t; +#define GRAALPY_MEM_SAMPLE_USEFUL_DEPTH (10) + +typedef struct { + void *ptr; + void *stack[GRAALPY_MEM_SAMPLE_USEFUL_DEPTH]; + size_t size; + size_t depth; + unsigned long long serial; + char operation; +} GraalPyMemSample_t; + /* Get an object's GC head */ #define AS_MEM_HEAD(o) ((mem_head_t *)(o)-1) /* Get the object given the GC head */ #define FROM_MEM_HEAD(g) ((void *)(((mem_head_t *)g)+1)) +#define GRAALPY_MEM_HEAD_MAGIC ((size_t)0x47505241574D454DULL) +#define GRAALPY_MEM_HEAD_POISON ((size_t)0xDDDDBAD0DDDDBAD0ULL) +#define GRAALPY_MEM_SAMPLE_RING_SIZE (4096) +#define GRAALPY_MEM_SAMPLE_HISTORY (8) +#define GRAALPY_MEM_SAMPLE_STACK_SKIP (2) #define MAX_COLLECTION_RETRIES (7) #define COLLECTION_DELAY_INCREMENT (50) @@ -72,6 +88,113 @@ typedef struct { } GraalPyMem_t; static GraalPyMem_t _GraalPyMem_State = { 0, 0, 0 }; +static GraalPyMemSample_t _GraalPyMem_Samples[GRAALPY_MEM_SAMPLE_RING_SIZE] = {{0}}; +static unsigned long long _GraalPyMem_SampleSerial = 0; +static size_t _GraalPyMem_SampleIndex = 0; + +static MUST_INLINE int +_GraalPyMem_PoisonOnFreeEnabled(void) +{ + return GraalPyPrivate_PoisonNativeMemoryOnFree(); +} + +static MUST_INLINE int +_GraalPyMem_SampleAllocSitesEnabled(void) +{ + return GraalPyPrivate_SampleNativeMemoryAllocSites(); +} + +static void +_GraalPyMem_CaptureSampleStack(GraalPyMemSample_t *sample) +{ + sample->depth = GraalPyPrivate_CaptureStacktrace(sample->stack, GRAALPY_MEM_SAMPLE_USEFUL_DEPTH, + GRAALPY_MEM_SAMPLE_STACK_SKIP); +} + +static void +_GraalPyMem_RecordSample(char operation, void *ptr, size_t size) +{ + if (UNLIKELY(ptr == NULL)) { + return; + } + if (LIKELY(!_GraalPyMem_SampleAllocSitesEnabled())) { + return; + } + + size_t index = _GraalPyMem_SampleIndex++ % GRAALPY_MEM_SAMPLE_RING_SIZE; + GraalPyMemSample_t *sample = &_GraalPyMem_Samples[index]; + sample->ptr = ptr; + sample->size = size; + sample->serial = ++_GraalPyMem_SampleSerial; + sample->operation = operation; + _GraalPyMem_CaptureSampleStack(sample); +} + +static void +_GraalPyMem_LogRecentSamples(const char *func, void *ptr) +{ + if (LIKELY(!_GraalPyMem_SampleAllocSitesEnabled())) { + return; + } + + size_t next_index = _GraalPyMem_SampleIndex; + int printed = 0; + for (size_t offset = 0; offset < GRAALPY_MEM_SAMPLE_RING_SIZE && printed < GRAALPY_MEM_SAMPLE_HISTORY; offset++) { + size_t index = (next_index + GRAALPY_MEM_SAMPLE_RING_SIZE - offset - 1) % GRAALPY_MEM_SAMPLE_RING_SIZE; + const GraalPyMemSample_t *sample = &_GraalPyMem_Samples[index]; + if (sample->ptr == ptr && sample->serial != 0) { + char prefix[128]; + GraalPyPrivate_Log(GRAALPY_LOG_INFO, + "%s: recent raw memory sample #%llu op=%c ptr=%p size=%lu depth=%lu\n", + func, sample->serial, sample->operation, sample->ptr, (unsigned long) sample->size, (unsigned long) sample->depth); + snprintf(prefix, sizeof(prefix), "%s: sample #%llu ", func, sample->serial); + GraalPyPrivate_LogCapturedStacktrace(GRAALPY_LOG_INFO, prefix, sample->stack, sample->depth); + printed++; + } + } +} + +static void +_GraalPyMem_InitHeader(mem_head_t *ptr_with_head, size_t size) +{ + ptr_with_head->size = size; + ptr_with_head->dummy = GRAALPY_MEM_HEAD_MAGIC; +} + +static void +_GraalPyMem_PoisonBlock(mem_head_t *ptr_with_head, size_t size) +{ + if (LIKELY(!_GraalPyMem_PoisonOnFreeEnabled())) { + return; + } + + memset(ptr_with_head, 0xDB, sizeof(mem_head_t) + size); + ptr_with_head->size = GRAALPY_MEM_HEAD_POISON; + ptr_with_head->dummy = GRAALPY_MEM_HEAD_POISON; +} + +static void +_GraalPyMem_FatalInvalidHeader(const char *func, void *ptr, const mem_head_t *ptr_with_head) +{ + const char *reason = (ptr_with_head->size == GRAALPY_MEM_HEAD_POISON && ptr_with_head->dummy == GRAALPY_MEM_HEAD_POISON) + ? "poisoned raw allocation header" + : "invalid raw allocation header"; + GraalPyPrivate_Log(GRAALPY_LOG_INFO, + "%s: %s for ptr=%p head=%p size=%lu dummy=0x%lx\n", + func, reason, ptr, ptr_with_head, (unsigned long) ptr_with_head->size, (unsigned long) ptr_with_head->dummy); + _GraalPyMem_LogRecentSamples(func, ptr); + Py_FatalError("invalid GraalPy raw allocation header"); +} + +static mem_head_t * +_GraalPyMem_GetValidatedHead(const char *func, void *ptr) +{ + mem_head_t *ptr_with_head = AS_MEM_HEAD(ptr); + if (UNLIKELY(ptr_with_head->dummy != GRAALPY_MEM_HEAD_MAGIC)) { + _GraalPyMem_FatalInvalidHeader(func, ptr, ptr_with_head); + } + return ptr_with_head; +} #if 0 // GraalPy change /* bpo-35053: Declare tracemalloc configuration here rather than @@ -325,7 +448,9 @@ PyMem_RawMalloc(size_t size) */ if (size > (size_t)PY_SSIZE_T_MAX) return NULL; - return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); + void *ptr = _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); + _GraalPyMem_RecordSample('m', ptr, size == 0 ? 1 : size); + return ptr; } void * @@ -334,7 +459,10 @@ PyMem_RawCalloc(size_t nelem, size_t elsize) /* see PyMem_RawMalloc() */ if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) return NULL; - return _PyMem_Raw.calloc(_PyMem_Raw.ctx, nelem, elsize); + void *ptr = _PyMem_Raw.calloc(_PyMem_Raw.ctx, nelem, elsize); + size_t nbytes = (nelem == 0 || elsize == 0) ? 1 : nelem * elsize; + _GraalPyMem_RecordSample('c', ptr, nbytes); + return ptr; } void* @@ -343,11 +471,14 @@ PyMem_RawRealloc(void *ptr, size_t new_size) /* see PyMem_RawMalloc() */ if (new_size > (size_t)PY_SSIZE_T_MAX) return NULL; - return _PyMem_Raw.realloc(_PyMem_Raw.ctx, ptr, new_size); + void *new_ptr = _PyMem_Raw.realloc(_PyMem_Raw.ctx, ptr, new_size); + _GraalPyMem_RecordSample('r', new_ptr, new_size == 0 ? 1 : new_size); + return new_ptr; } void PyMem_RawFree(void *ptr) { + _GraalPyMem_RecordSample('f', ptr, 0); _PyMem_Raw.free(_PyMem_Raw.ctx, ptr); } @@ -381,7 +512,7 @@ _GraalPyMem_PrepareAlloc(GraalPyMem_t *state, size_t size) state->native_memory_gc_barrier = GraalPyPrivate_GetInitialNativeMemory(); continue; } - GraalPyPrivate_Log(PY_TRUFFLE_LOG_CONFIG, + GraalPyPrivate_Log(GRAALPY_LOG_CONFIG, "%s: exceeding native_memory_gc_barrier (%lu) with allocation of size %lu, current allocated_memory: %lu\n", __func__, state->native_memory_gc_barrier, size, state->allocated_memory); @@ -401,12 +532,12 @@ _GraalPyMem_PrepareAlloc(GraalPyMem_t *state, size_t size) if (state->native_memory_gc_barrier > state->max_native_memory) { state->native_memory_gc_barrier = state->max_native_memory; } - GraalPyPrivate_Log(PY_TRUFFLE_LOG_CONFIG, + GraalPyPrivate_Log(GRAALPY_LOG_CONFIG, "%s: enlarging native_memory_gc_barrier to %lu\n", __func__, state->native_memory_gc_barrier); } else { - GraalPyPrivate_Log(PY_TRUFFLE_LOG_INFO, + GraalPyPrivate_Log(GRAALPY_LOG_INFO, "%s: native memory exhausted while allocating %lu bytes\n", __func__, size); return 1; @@ -426,11 +557,16 @@ _GraalPyMem_RawMalloc(void *ctx, size_t size) To solve these problems, allocate an extra byte. */ if (size == 0) size = 1; - if (_GraalPyMem_PrepareAlloc((GraalPyMem_t*) ctx, size)) { + GraalPyMem_t *state = (GraalPyMem_t *)ctx; + if (_GraalPyMem_PrepareAlloc(state, size)) { return NULL; } mem_head_t *ptr_with_head = (mem_head_t *)malloc(size + sizeof(mem_head_t)); - ptr_with_head->size = size; + if (ptr_with_head == NULL) { + state->allocated_memory -= size; + return NULL; + } + _GraalPyMem_InitHeader(ptr_with_head, size); return FROM_MEM_HEAD(ptr_with_head); } @@ -450,15 +586,20 @@ _GraalPyMem_RawCalloc(void *ctx, size_t nelem, size_t elsize) elsize = 1; } size_t nbytes = nelem * elsize; - if (_GraalPyMem_PrepareAlloc((GraalPyMem_t*) ctx, nbytes)) { + GraalPyMem_t *state = (GraalPyMem_t *)ctx; + if (_GraalPyMem_PrepareAlloc(state, nbytes)) { return NULL; } /* We cannot use 'calloc' because we need to allocate following layout: [ mem_head_t ] [ e_0 ] [ e_1 ] [ e_2 ] ... [ n_nelem ] */ size_t total = nbytes + sizeof(mem_head_t); mem_head_t *ptr_with_head = (mem_head_t *)malloc(total); + if (ptr_with_head == NULL) { + state->allocated_memory -= nbytes; + return NULL; + } memset(ptr_with_head, 0, total); - ptr_with_head->size = nbytes; + _GraalPyMem_InitHeader(ptr_with_head, nbytes); return FROM_MEM_HEAD(ptr_with_head); } @@ -474,25 +615,33 @@ _GraalPyMem_RawRealloc(void *ctx, void *ptr, size_t size) size = 1; if (ptr != NULL) { - old = AS_MEM_HEAD(ptr); + old = _GraalPyMem_GetValidatedHead(__func__, ptr); old_size = old->size; } else { old = NULL; old_size = 0; } - // account for the difference in size - if (old_size >= size) { - /* In case of "shrinking", just subtract the counter but don't trigger - the Java GC. */ - state->allocated_memory -= size; - } else if (_GraalPyMem_PrepareAlloc(state, size - old_size)) { + if (old_size < size && _GraalPyMem_PrepareAlloc(state, size - old_size)) { return NULL; } mem_head_t *ptr_with_head = (mem_head_t *)realloc(old, size + sizeof(mem_head_t)); - ptr_with_head->size = size; + if (ptr_with_head == NULL) { + if (old_size < size) { + state->allocated_memory -= size - old_size; + } + return NULL; + } + + if (old_size > size) { + /* In case of "shrinking", just subtract the difference but don't + trigger the Java GC. */ + state->allocated_memory -= old_size - size; + } + + _GraalPyMem_InitHeader(ptr_with_head, size); return FROM_MEM_HEAD(ptr_with_head); } @@ -506,14 +655,15 @@ _GraalPyMem_RawFree(void *ctx, void *ptr) if (ptr == NULL) return; GraalPyMem_t *state = (GraalPyMem_t *)ctx; - mem_head_t *ptr_with_head = AS_MEM_HEAD(ptr); + mem_head_t *ptr_with_head = _GraalPyMem_GetValidatedHead(__func__, ptr); const size_t size = ptr_with_head->size; if (state->allocated_memory < size) { - GraalPyPrivate_Log(PY_TRUFFLE_LOG_INFO, + GraalPyPrivate_Log(GRAALPY_LOG_INFO, "%s: freed memory size (%lu) is larger than allocated memory size (%lu)\n", __func__, size, state->allocated_memory); state->allocated_memory = size; } state->allocated_memory -= size; + _GraalPyMem_PoisonBlock(ptr_with_head, size); free(ptr_with_head); } diff --git a/graalpython/com.oracle.graal.python.cext/src/pyhash.c b/graalpython/com.oracle.graal.python.cext/src/pyhash.c index b7ad58cf07..1d5b4c1182 100644 --- a/graalpython/com.oracle.graal.python.cext/src/pyhash.c +++ b/graalpython/com.oracle.graal.python.cext/src/pyhash.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 @@ -489,7 +489,7 @@ long _PyHASH_INF; long _PyHASH_MODULUS; long _PyHASH_IMAG; -void +Py_LOCAL_SYMBOL void initialize_hashes() { _PyHASH_INF = GraalPyPrivate_HashConstant(0); diff --git a/graalpython/com.oracle.graal.python.cext/src/pylifecycle.c b/graalpython/com.oracle.graal.python.cext/src/pylifecycle.c index 77e295c1fc..16f474b73d 100644 --- a/graalpython/com.oracle.graal.python.cext/src/pylifecycle.c +++ b/graalpython/com.oracle.graal.python.cext/src/pylifecycle.c @@ -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 @@ -73,6 +73,36 @@ _Py_IsFinalizing(void) return graalpy_finalizing; } +static void +graalpy_fork_not_supported(void) +{ + PyErr_SetString(PyExc_SystemError, "fork is not supported by GraalPy"); +} + +void +PyOS_BeforeFork(void) +{ + graalpy_fork_not_supported(); +} + +void +PyOS_AfterFork_Parent(void) +{ + graalpy_fork_not_supported(); +} + +void +PyOS_AfterFork_Child(void) +{ + graalpy_fork_not_supported(); +} + +void +PyOS_AfterFork(void) +{ + PyOS_AfterFork_Child(); +} + void _Py_NO_RETURN _Py_FatalErrorFunc(const char *func, const char *msg) { const char *prefix1 = "", *prefix2 = ""; if (func) { diff --git a/graalpython/com.oracle.graal.python.cext/src/pystate.c b/graalpython/com.oracle.graal.python.cext/src/pystate.c index b54a724306..eb28b2c479 100644 --- a/graalpython/com.oracle.graal.python.cext/src/pystate.c +++ b/graalpython/com.oracle.graal.python.cext/src/pystate.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 @@ -29,9 +29,7 @@ #endif // GraalPy change #include "capi.h" -#include -extern TruffleContext* TRUFFLE_CONTEXT; static THREAD_LOCAL int graalpy_attached_thread = 0; static THREAD_LOCAL int graalpy_gilstate_counter = 0; @@ -84,9 +82,16 @@ static inline PyThreadState * _get_thread_state() { PyThreadState *ts = tstate_current; if (UNLIKELY(ts == NULL)) { - ts = GraalPyPrivate_ThreadState_Get(&tstate_current); - tstate_current = ts; + /* + * Very unlikely fallback: this can happen if another thread initializes the C API while + * the current thread is attached to Python but blocked and therefore misses eager + * initialization of its native 'tstate_current' TLS slot. + */ + ts = GraalPyPrivate_ThreadState_Get(&tstate_current); + assert(ts != NULL); + tstate_current = ts; } + assert(ts != NULL); return ts; } @@ -1416,6 +1421,7 @@ init_threadstate(PyThreadState *tstate, tstate->datastack_top = NULL; tstate->datastack_limit = NULL; tstate->what_event = -1; + graalpy_initialize_thread_state_singletons(tstate); tstate->_status.initialized = 1; } @@ -2283,22 +2289,22 @@ PyGILState_Check(void) return (tstate == gilstate_tss_get(runtime)); #else // GraalPy change - int attached = 0; /* * PyGILState_Check is allowed to be called from a new thread that didn't yet setup the GIL. - * If we don't attach the thread ourselves, the upcall will work because NFI will attach - * the thread automatically, but it won't create the context which would then break - * subsequent PyGILState_Ensure. + * We must attach it before calling into Java so the upcall has an entered polyglot context. */ - if (TRUFFLE_CONTEXT) { - if ((*TRUFFLE_CONTEXT)->getTruffleEnv(TRUFFLE_CONTEXT) == NULL) { - (*TRUFFLE_CONTEXT)->attachCurrentThread(TRUFFLE_CONTEXT); - attached = 1; + int attached = 0; + if (graalpy_attach_native_thread) { + attached = graalpy_attach_native_thread(); + if (attached == GRAALPY_ATTACH_NATIVE_FAILED) { + return 0; } + } else { + return 0; } int ret = GraalPyPrivate_GILState_Check(); - if (attached) { - (*TRUFFLE_CONTEXT)->detachCurrentThread(TRUFFLE_CONTEXT); + if (attached == GRAALPY_ATTACH_NATIVE_OWNED && graalpy_detach_native_thread) { + graalpy_detach_native_thread(); } return ret; #endif // GraalPy change @@ -2355,13 +2361,18 @@ PyGILState_Ensure(void) return has_gil ? PyGILState_LOCKED : PyGILState_UNLOCKED; #else // GraalPy change - if (TRUFFLE_CONTEXT) { - if ((*TRUFFLE_CONTEXT)->getTruffleEnv(TRUFFLE_CONTEXT) == NULL) { - (*TRUFFLE_CONTEXT)->attachCurrentThread(TRUFFLE_CONTEXT); + if (!graalpy_attach_native_thread) { + Py_FatalError("PyGILState_Ensure called before GraalPy C API initialization"); + } + if (!graalpy_attached_thread) { + int attached = graalpy_attach_native_thread(); + if (attached == GRAALPY_ATTACH_NATIVE_FAILED) { + Py_FatalError("Could not attach native thread to the polyglot context"); + } else if (attached == GRAALPY_ATTACH_NATIVE_OWNED) { graalpy_attached_thread = 1; } - graalpy_gilstate_counter++; } + graalpy_gilstate_counter++; return GraalPyPrivate_GILState_Ensure() ? PyGILState_UNLOCKED : PyGILState_LOCKED; #endif // GraalPy change } @@ -2421,20 +2432,20 @@ PyGILState_Release(PyGILState_STATE oldstate) if (oldstate == PyGILState_UNLOCKED) { GraalPyPrivate_GILState_Release(); } - if (TRUFFLE_CONTEXT) { - graalpy_gilstate_counter--; - if (graalpy_gilstate_counter == 0 && graalpy_attached_thread) { - GraalPyPrivate_BeforeThreadDetach(); - (*TRUFFLE_CONTEXT)->detachCurrentThread(TRUFFLE_CONTEXT); - graalpy_attached_thread = 0; - /* - * The thread state on the Java-side is cleared in GraalPyPrivate_BeforeThreadDetach. - * As part of that the tstate_current pointer should have been set to NULL to make - * sure to fetch a fresh pointer the next time we attach. Just to be sure, we clear - * it here too: - */ - tstate_current = NULL; + graalpy_gilstate_counter--; + if (graalpy_gilstate_counter == 0 && graalpy_attached_thread) { + GraalPyPrivate_BeforeThreadDetach(); + if (graalpy_detach_native_thread) { + graalpy_detach_native_thread(); } + graalpy_attached_thread = 0; + /* + * The thread state on the Java-side is cleared in GraalPyPrivate_BeforeThreadDetach. + * As part of that the tstate_current pointer should have been set to NULL to make + * sure to fetch a fresh pointer the next time we attach. Just to be sure, we clear + * it here too: + */ + tstate_current = NULL; } #endif // GraalPy change } diff --git a/graalpython/com.oracle.graal.python.cext/src/sysmodule.c b/graalpython/com.oracle.graal.python.cext/src/sysmodule.c index 3796e7fb4d..9c7df408ea 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,9 +40,36 @@ */ #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; + PyObject *args; + if (argFormat == NULL) { + args = PyTuple_New(0); + if (args == NULL) { + return -1; + } + } else { + va_list va; + va_start(va, argFormat); + args = Py_VaBuildValue(argFormat, va); + va_end(va); + if (args == NULL) { + return -1; + } + if (!PyTuple_Check(args)) { + PyObject *tmp = PyTuple_Pack(1, args); + Py_DECREF(args); + if (tmp == NULL) { + return -1; + } + args = tmp; + } + } + int result = GraalPyPrivate_Sys_Audit(event, args); + Py_DECREF(args); + return result; } static void @@ -84,6 +111,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 +147,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 +158,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..5b4730b249 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" @@ -108,6 +105,66 @@ PyTuple_Size(PyObject *op) } #endif // GraalPy change +/* Allocate an uninitialized tuple object. Before making it public, following + steps must be done: + + - Initialize its items. + - Call _PyObject_GC_TRACK() on it. + + GraalPy change: unlike CPython, this does not use the tuple freelist. +*/ +static PyTupleObject * +tuple_alloc(Py_ssize_t size) +{ + if (size < 0) { + PyErr_BadInternalCall(); + return NULL; + } + /* Check for overflow */ + if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - (sizeof(PyTupleObject) - + sizeof(PyObject *))) / sizeof(PyObject *)) { + return (PyTupleObject *)PyErr_NoMemory(); + } + PyTupleObject *op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size); + if (op == NULL) { + return NULL; + } + return op; +} + +static inline PyObject * +tuple_get_empty(void) +{ + /* + * GraalPy change: allocate a native empty tuple instead of returning CPython's static + * singleton, so PyTuple_New consistently returns native tuples. + */ + PyTupleObject *op = tuple_alloc(0); + if (op == NULL) { + return NULL; + } + _PyObject_GC_TRACK(op); + return (PyObject *)op; +} + +PyObject * +PyTuple_New(Py_ssize_t size) +{ + PyTupleObject *op; + if (size == 0) { + return tuple_get_empty(); + } + op = tuple_alloc(size); + if (op == NULL) { + return NULL; + } + for (Py_ssize_t i = 0; i < size; i++) { + op->ob_item[i] = NULL; + } + _PyObject_GC_TRACK(op); + return (PyObject *)op; +} + PyObject * PyTuple_GetItem(PyObject *op, Py_ssize_t i) { // GraalPy change: different implementation @@ -203,13 +260,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 +281,7 @@ tupledealloc(PyTupleObject *op) assert(!PyTuple_CheckExact(op)); #endif } +#endif // GraalPy change PyObject_GC_UnTrack(op); Py_TRASHCAN_BEGIN(op, tupledealloc) @@ -233,13 +291,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 +773,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 +800,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 +860,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; +} + +GraalPy_CAPI_HELPER_SYMBOL PyObject * // GraalPy change: helper-table entry for downcall, rename +GraalPyPrivate_Tuple_SubtypeNew(PyTypeObject *type, PyObject *iterable) +{ + return tuple_subtype_new(type, iterable); } #if 0 // GraalPy change @@ -900,7 +1005,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 +1039,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 +1410,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..c15d299f18 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 @@ -1803,10 +1803,10 @@ traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg) return 0; } -/* GraalPy change: replaced 'static' with 'PyAPI_FUNC' and renamed because we lookup the - * symbol in Java to use it if a type receives 'toNative'. +/* GraalPy change: renamed and exposed through the native helper table because Java uses + * it if a type receives 'toNative'. */ -PyAPI_FUNC(int) +GraalPy_CAPI_HELPER_SYMBOL int GraalPyPrivate_SubtypeTraverse(PyObject *self, visitproc visit, void *arg) { PyTypeObject *type, *base; @@ -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); } @@ -7316,6 +7321,9 @@ type_ready_mro(PyTypeObject *type) } #else // GraalPy change PyObject* mro = GraalPyPrivate_Compute_Mro(type, type->tp_name); + if (mro == NULL) { + return -1; + } type->tp_mro = mro; #endif // GraalPy change return 0; @@ -7582,6 +7590,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 +7662,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..36be194195 100644 --- a/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c @@ -127,6 +127,33 @@ extern "C" { # define _PyUnicode_CHECK(op) PyUnicode_Check(op) #endif +static inline Py_ssize_t * +GraalPyPrivate_Unicode_LengthPtr(PyObject *op) +{ + if (points_to_py_handle_space(op)) { + return &((GraalPyUnicodeObject *) pointer_to_stub(op))->length; + } + return &_PyASCIIObject_CAST(op)->length; +} + +static inline Py_hash_t * +GraalPyPrivate_Unicode_HashPtr(PyObject *op) +{ + if (points_to_py_handle_space(op)) { + return &((GraalPyUnicodeObject *) pointer_to_stub(op))->hash; + } + return &_PyASCIIObject_CAST(op)->hash; +} + +static inline void ** +GraalPyPrivate_Unicode_DataPtr(PyObject *op) +{ + if (points_to_py_handle_space(op)) { + return &((GraalPyUnicodeObject *) pointer_to_stub(op))->data; + } + return &_PyUnicodeObject_CAST(op)->data.any; +} + #define _PyUnicode_UTF8(op) \ (_PyCompactUnicodeObject_CAST(op)->utf8) #define PyUnicode_UTF8(op) \ @@ -143,19 +170,19 @@ extern "C" { _PyUnicode_UTF8_LENGTH(op)) #define _PyUnicode_LENGTH(op) \ - (_PyASCIIObject_CAST(op)->length) + (*GraalPyPrivate_Unicode_LengthPtr(_PyObject_CAST(op))) #define _PyUnicode_STATE(op) \ (_PyASCIIObject_CAST(op)->state) #define _PyUnicode_HASH(op) \ - (_PyASCIIObject_CAST(op)->hash) + (*GraalPyPrivate_Unicode_HashPtr(_PyObject_CAST(op))) #define _PyUnicode_KIND(op) \ (assert(_PyUnicode_CHECK(op)), \ - _PyASCIIObject_CAST(op)->state.kind) + PyUnicode_KIND(op)) #define _PyUnicode_GET_LENGTH(op) \ (assert(_PyUnicode_CHECK(op)), \ - _PyASCIIObject_CAST(op)->length) + PyUnicode_GET_LENGTH(op)) #define _PyUnicode_DATA_ANY(op) \ - (_PyUnicodeObject_CAST(op)->data.any) + (*GraalPyPrivate_Unicode_DataPtr(_PyObject_CAST(op))) #define _PyUnicode_SHARE_UTF8(op) \ (assert(_PyUnicode_CHECK(op)), \ @@ -666,6 +693,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) @@ -1354,22 +1385,24 @@ PyObject * PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar) { // GraalPy change: different implementation - /* add one to size for the null character */ + if (size < 0) { + PyErr_SetString(PyExc_SystemError, + "Negative size passed to PyUnicode_New"); + return NULL; + } if (maxchar < 128) { - /* We intentionally use 'size' (which is one element less than the allocated array) - * because interop users should not see the null character. */ - return GraalPyPrivate_Unicode_New((Py_UCS1 *) calloc(size + 1, PyUnicode_1BYTE_KIND), size, PyUnicode_1BYTE_KIND, 1); + return GraalPyPrivate_Unicode_New(size, PyUnicode_1BYTE_KIND, 1); } else if (maxchar < 256) { - return GraalPyPrivate_Unicode_New((Py_UCS1 *) calloc(size + 1, PyUnicode_1BYTE_KIND), size, PyUnicode_1BYTE_KIND, 0); + return GraalPyPrivate_Unicode_New(size, PyUnicode_1BYTE_KIND, 0); } else if (maxchar < 65536) { - return GraalPyPrivate_Unicode_New((Py_UCS2 *) calloc(size + 1, PyUnicode_2BYTE_KIND), size, PyUnicode_2BYTE_KIND, 0); + return GraalPyPrivate_Unicode_New(size, PyUnicode_2BYTE_KIND, 0); } else { if (maxchar > MAX_UNICODE) { PyErr_SetString(PyExc_SystemError, "invalid maximum character passed to PyUnicode_New"); return NULL; } - return GraalPyPrivate_Unicode_New((Py_UCS4 *) calloc(size + 1, PyUnicode_4BYTE_KIND), size, PyUnicode_4BYTE_KIND, 0); + return GraalPyPrivate_Unicode_New(size, PyUnicode_4BYTE_KIND, 0); } /* should never be reached */ return NULL; @@ -1672,8 +1705,8 @@ find_maxchar_surrogates(const wchar_t *begin, const wchar_t *end, return 0; } -// GraalPy change: export -PyAPI_FUNC(void) +// GraalPy change: hidden for capi.c +Py_LOCAL_SYMBOL void unicode_dealloc(PyObject *unicode) { #ifdef Py_DEBUG @@ -3676,8 +3709,7 @@ PyObject* PyUnicode_DecodeFSDefaultAndSize(const char *s, Py_ssize_t size) { // GraalPy change: different implementation - // TODO: this implementation does not honor Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors - return GraalPyPrivate_Unicode_FromUTF((void*) s, size, 1); + return GraalPyPrivate_Unicode_DecodeFSDefaultAndSize((void*) s, size); } @@ -14164,7 +14196,7 @@ unicode_new_impl(PyTypeObject *type, PyObject *x, const char *encoding, } #endif // GraalPy change -PyAPI_FUNC(PyObject *) // GraalPy change: export for downcall, rename +GraalPy_CAPI_HELPER_SYMBOL PyObject * // GraalPy change: helper-table entry for downcall, rename GraalPyPrivate_Unicode_SubtypeNew(PyTypeObject *type, PyObject *unicode) { PyObject *self; @@ -14190,7 +14222,7 @@ GraalPyPrivate_Unicode_SubtypeNew(PyTypeObject *type, PyObject *unicode) _PyUnicode_STATE(self).kind = kind; _PyUnicode_STATE(self).compact = 0; // GraalPy change - _PyUnicode_STATE(self).ascii = GET_SLOT_SPECIAL(unicode, PyASCIIObject, state_ascii, state.ascii); + _PyUnicode_STATE(self).ascii = PyUnicode_IS_ASCII(unicode); _PyUnicode_STATE(self).statically_allocated = 0; _PyUnicode_UTF8_LENGTH(self) = 0; _PyUnicode_UTF8(self) = NULL; @@ -14822,8 +14854,8 @@ unicode_ascii_iter_next(unicodeiterobject *it) Py_UCS1 chr = (Py_UCS1)PyUnicode_READ(PyUnicode_1BYTE_KIND, data, it->it_index); it->it_index++; - PyObject *item = (PyObject*)&_Py_SINGLETON(strings).ascii[chr]; - return Py_NewRef(item); + char ch = (char)chr; + return PyUnicode_FromStringAndSize(&ch, 1); } it->it_seq = NULL; Py_DECREF(seq); @@ -15238,28 +15270,129 @@ PyInit__string(void) #endif // GraalPy change // GraalPy additions +/* Keep in sync with CApiTransitions.encodeGraalPyUnicodeObjectAscii. */ +static inline uint64_t +GraalPyUnicodeObject_EncodeAscii(unsigned int is_ascii) +{ + return is_ascii ? GRAALPY_UNICODE_IS_ASCII_FLAG : 0; +} + +/* Keep in sync with CApiTransitions.encodeGraalPyUnicodeObjectInterned. */ +static inline uint64_t +GraalPyUnicodeObject_EncodeInterned(unsigned int interned) +{ + return (uint64_t) interned << GRAALPY_UNICODE_INTERN_STATE_SHIFT; +} + +/* Keep in sync with CApiTransitions.createGraalPyUnicodeObjectState. */ +static inline uint64_t +GraalPyUnicodeObject_CreateState(int kind, unsigned int is_ascii, unsigned int interned) +{ + return kind | GraalPyUnicodeObject_EncodeAscii(is_ascii) | GraalPyUnicodeObject_EncodeInterned(interned); +} + +/* Keep in sync with CApiTransitions.getGraalPyUnicodeObjectInternedFromState. */ +static inline unsigned int +GraalPyUnicodeObject_GetInternedFromState(uint64_t state) +{ + return (state & GRAALPY_UNICODE_INTERN_STATE_MASK) >> GRAALPY_UNICODE_INTERN_STATE_SHIFT; +} + +/* Keep in sync with CApiTransitions.updateGraalPyUnicodeObjectInterned. */ +static inline uint64_t +GraalPyUnicodeObject_UpdateInterned(uint64_t state, unsigned int interned) +{ + return (state & ~GRAALPY_UNICODE_INTERN_STATE_MASK) | GraalPyUnicodeObject_EncodeInterned(interned); +} + +/* Keep in sync with CApiTransitions.isGraalPyUnicodeObjectAsciiFromState. */ +static inline unsigned int +GraalPyUnicodeObject_IsAsciiFromState(uint64_t state) +{ + return (state & GRAALPY_UNICODE_IS_ASCII_FLAG) != 0; +} + +/* Keep in sync with CApiTransitions.getGraalPyUnicodeObjectKindFromState. */ +static inline int +GraalPyUnicodeObject_GetKindFromState(uint64_t state) +{ + return state & GRAALPY_UNICODE_KIND_MASK; +} + +/* Keep in sync with CApiTransitions.getGraalPyUnicodeObjectInternedFromState. */ +static inline unsigned int +GraalPyUnicodeObject_GetInterned(GraalPyUnicodeObject *unicode) +{ + return GraalPyUnicodeObject_GetInternedFromState(unicode->state); +} + +/* Keep in sync with CApiTransitions.setGraalPyUnicodeObjectInterned. */ +static inline void +GraalPyUnicodeObject_SetInterned(GraalPyUnicodeObject *unicode, unsigned int interned) +{ + unicode->state = GraalPyUnicodeObject_UpdateInterned(unicode->state, interned); +} + +/* Keep in sync with CApiTransitions.isGraalPyUnicodeObjectAsciiFromState. */ +static inline unsigned int +GraalPyUnicodeObject_IsAscii(GraalPyUnicodeObject *unicode) +{ + return GraalPyUnicodeObject_IsAsciiFromState(unicode->state); +} + +/* Keep in sync with CApiTransitions.getGraalPyUnicodeObjectKind. */ +static inline int +GraalPyUnicodeObject_GetKind(GraalPyUnicodeObject *unicode) +{ + return GraalPyUnicodeObject_GetKindFromState(unicode->state); +} + unsigned int GraalPyUnicode_CHECK_INTERNED(PyObject *op) { - return GET_SLOT_SPECIAL(op, PyASCIIObject, state_interned, state.interned); + if (points_to_py_handle_space(op)) { + GraalPyUnicodeObject *unicode = (GraalPyUnicodeObject *) pointer_to_stub(op); + unsigned int interned = GraalPyUnicodeObject_GetInterned(unicode); + if (interned == GRAALPY_UNICODE_INTERN_STATE_UNDETERMINED) { + interned = GraalPyPrivate_Unicode_CheckInterned(op) ? GRAALPY_UNICODE_INTERN_STATE_INTERNED : GRAALPY_UNICODE_INTERN_STATE_NOT_INTERNED; + GraalPyUnicodeObject_SetInterned(unicode, interned); + } + return interned == GRAALPY_UNICODE_INTERN_STATE_INTERNED ? SSTATE_INTERNED_MORTAL : SSTATE_NOT_INTERNED; + } + return _PyASCIIObject_CAST(op)->state.interned; } Py_ssize_t GraalPyUnicode_GET_LENGTH(PyObject* op) { - return GraalPyPrivate_GET_PyASCIIObject_length(op); + if (points_to_py_handle_space(op)) { + return ((GraalPyUnicodeObject *) pointer_to_stub(op))->length; + } + return _PyASCIIObject_CAST(op)->length; } unsigned int GraalPyUnicode_IS_ASCII(PyObject* op) { - return GET_SLOT_SPECIAL(op, PyASCIIObject, state_ascii, state.ascii); + if (points_to_py_handle_space(op)) { + return GraalPyUnicodeObject_IsAscii((GraalPyUnicodeObject *) pointer_to_stub(op)); + } + return _PyASCIIObject_CAST(op)->state.ascii; } unsigned int GraalPyUnicode_IS_COMPACT(PyObject* op) { - return GET_SLOT_SPECIAL(op, PyASCIIObject, state_compact, state.compact); + if (points_to_py_handle_space(op)) { + return 0; + } + return _PyASCIIObject_CAST(op)->state.compact; } int GraalPyUnicode_KIND(PyObject* op) { - return GET_SLOT_SPECIAL(op, PyASCIIObject, state_kind, state.kind); + if (points_to_py_handle_space(op)) { + return GraalPyUnicodeObject_GetKind((GraalPyUnicodeObject *) pointer_to_stub(op)); + } + return _PyASCIIObject_CAST(op)->state.kind; } void* GraalPyUnicode_NONCOMPACT_DATA(PyObject* op) { - return GET_SLOT_SPECIAL(op, PyUnicodeObject, data, data.any); + if (points_to_py_handle_space(op)) { + return ((GraalPyUnicodeObject *) pointer_to_stub(op))->data; + } + return _PyUnicodeObject_CAST(op)->data.any; } #ifdef __cplusplus diff --git a/graalpython/com.oracle.graal.python.frozen/freeze_modules.py b/graalpython/com.oracle.graal.python.frozen/freeze_modules.py index ce4e6c5c5e..62415caaad 100644 --- a/graalpython/com.oracle.graal.python.frozen/freeze_modules.py +++ b/graalpython/com.oracle.graal.python.frozen/freeze_modules.py @@ -112,7 +112,6 @@ def add_graalpython_core(): l = [] l.append("polyglot.arrow : polyglot.arrow = " + os.path.join(lib_graalpython, "modules/_polyglot_arrow.py")) for name in [ - "modules/_sysconfigdata", "modules/_polyglot", "modules/_polyglot_time", ]: @@ -124,8 +123,6 @@ def add_graalpython_core(): "_sre", "_sysconfig", "java", - "pip_hook", - "_nt", ]: modname = f"graalpy.{os.path.basename(name)}" modpath = os.path.join(lib_graalpython, f"{name}.py") diff --git a/graalpython/com.oracle.graal.python.pegparser.generator/CMakeLists.txt b/graalpython/com.oracle.graal.python.pegparser.generator/CMakeLists.txt index c029f65407..d2e122430c 100644 --- a/graalpython/com.oracle.graal.python.pegparser.generator/CMakeLists.txt +++ b/graalpython/com.oracle.graal.python.pegparser.generator/CMakeLists.txt @@ -34,12 +34,12 @@ set(ASDL_STAMP "Python.asdl.stamp") add_custom_command( OUTPUT "${PARSER_TARGET}" - COMMAND ${PYTHON_EXE} "${CMAKE_CURRENT_LIST_DIR}/main_parser_gen.py" "${GRAMMAR}" "${TOKENS}" "${PARSER_TARGET}" + COMMAND "${PYTHON_EXE}" "${CMAKE_CURRENT_LIST_DIR}/main_parser_gen.py" "${GRAMMAR}" "${TOKENS}" "${PARSER_TARGET}" DEPENDS "${CMAKE_CURRENT_LIST_DIR}/main_parser_gen.py" "${GRAMMAR}" "${TOKENS}" ${PEGEN_FILES} ${PEGJAVA_FILES}) add_custom_command( OUTPUT "${ASDL_STAMP}" - COMMAND ${PYTHON_EXE} "${CMAKE_CURRENT_LIST_DIR}/main_asdl_gen.py" "${ASDL}" --sst-path "${PEGPARSER_SRC_PATH}" --ast-path "${GRAALPY_SRC_PATH}" --stamp "${ASDL_STAMP}" + COMMAND "${PYTHON_EXE}" "${CMAKE_CURRENT_LIST_DIR}/main_asdl_gen.py" "${ASDL}" --sst-path "${PEGPARSER_SRC_PATH}" --ast-path "${GRAALPY_SRC_PATH}" --stamp "${ASDL_STAMP}" DEPENDS "${CMAKE_CURRENT_LIST_DIR}/main_asdl_gen.py" "${ASDL}" ${ASDL_FILES}) add_custom_target( diff --git a/graalpython/com.oracle.graal.python.pegparser/pom.xml b/graalpython/com.oracle.graal.python.pegparser/pom.xml new file mode 100644 index 0000000000..a6dbf87d88 --- /dev/null +++ b/graalpython/com.oracle.graal.python.pegparser/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + + org.graalvm.python.ide + graalpy-source-workspace + 1.0-SNAPSHOT + ../../pom.xml + + + com.oracle.graal.python.pegparser + jar + + + + org.graalvm.shadowed + icu4j + + + diff --git a/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/tokenizer/SourceRange.java b/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/tokenizer/SourceRange.java index 5bc0e0d9c1..14c494e99b 100644 --- a/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/tokenizer/SourceRange.java +++ b/graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/tokenizer/SourceRange.java @@ -66,7 +66,7 @@ public SourceRange withEnd(int newEndLine, int newEndColumn) { public SourceRange startLineShiftColumn(int shift) { assert shift >= 0; - if (shift == 0) { + if (shift == 0 || this == ARTIFICIAL_RANGE) { return this; } return new SourceRange(startLine, startColumn, startLine, startColumn + shift); diff --git a/graalpython/com.oracle.graal.python.processor/pom.xml b/graalpython/com.oracle.graal.python.processor/pom.xml new file mode 100644 index 0000000000..061e5e11bd --- /dev/null +++ b/graalpython/com.oracle.graal.python.processor/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + + org.graalvm.python.ide + graalpy-source-workspace + 1.0-SNAPSHOT + ../../pom.xml + + + com.oracle.graal.python.processor + jar + + + + ${project.groupId} + com.oracle.graal.python.annotations + + + diff --git a/graalpython/com.oracle.graal.python.processor/src/META-INF/services/javax.annotation.processing.Processor b/graalpython/com.oracle.graal.python.processor/src/META-INF/services/javax.annotation.processing.Processor index 84137ac45b..c56a7e9bd4 100644 --- a/graalpython/com.oracle.graal.python.processor/src/META-INF/services/javax.annotation.processing.Processor +++ b/graalpython/com.oracle.graal.python.processor/src/META-INF/services/javax.annotation.processing.Processor @@ -1,4 +1,5 @@ com.oracle.graal.python.processor.ArgumentClinicProcessor com.oracle.graal.python.processor.GenerateEnumConstantsProcessor +com.oracle.graal.python.processor.GenerateNativeDowncallsProcessor com.oracle.graal.python.processor.CApiBuiltinsProcessor -com.oracle.graal.python.processor.SlotsProcessor \ No newline at end of file +com.oracle.graal.python.processor.SlotsProcessor 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..c19609436f 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 @@ -40,19 +40,25 @@ */ package com.oracle.graal.python.processor; +import static com.oracle.graal.python.processor.NativeDowncallMethodHandleGenerator.argName; + import java.io.IOException; import java.nio.file.Files; 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,22 +70,36 @@ 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.CApiConstant; 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.oracle.graal.python.annotations.CApiUpcallTarget; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePathScanner; 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 +115,10 @@ public Object visitVariable(VariableTree variableTree, Trees trees) { private Map signatureToArgDescriptor = new HashMap<>(); private Map argDescriptorToInitializer = new HashMap<>(); + + private Map externalFunctionSignatureToInitializer = new HashMap<>(); + private Map nativeCAPISymbolToInitializer = new HashMap<>(); + private Trees trees; private String getFieldInitializer(VariableElement theField) { @@ -115,6 +139,34 @@ 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)); + + } + + private String getNativeCAPISymbolInitializer(VariableElement theField) { + if (trees == null) { + return ""; + } + if (nativeCAPISymbolToInitializer.isEmpty()) { + var codeScanner = new ArgDescriptorsTreeScanner(); + var tp = trees.getPath(theField.getEnclosingElement()); + codeScanner.initializerMap = nativeCAPISymbolToInitializer; + codeScanner.scan(tp, this.trees); + } + return nativeCAPISymbolToInitializer.get(name(theField)); + } + @Override public synchronized void init(ProcessingEnvironment pe) { super.init(pe); @@ -141,6 +193,12 @@ private static String getCSignature(String initializer) { } } + private static String[] splitInitializerArgs(String initializer) { + int start = initializer.indexOf('('); + int end = initializer.lastIndexOf(')'); + return initializer.substring(start + 1, end).split(","); + } + private static boolean isVarArgs(VariableElement obj) { return name(obj).equals("VARARGS"); } @@ -155,12 +213,27 @@ 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(Element obj) { + return obj.getSimpleName().toString(); } - private static String name(VariableElement obj) { - return obj.getSimpleName().toString(); + private static String formatCConstantValue(VariableElement constant, Object value) { + TypeKind kind = constant.asType().getKind(); + return switch (kind) { + case BOOLEAN -> ((Boolean) value) ? "1" : "0"; + case BYTE, SHORT, INT -> value.toString(); + case LONG -> value + "LL"; + case CHAR -> Integer.toString((Character) value); + case FLOAT -> { + float f = (Float) value; + yield Float.isFinite(f) ? Float.toString(f) + "f" : null; + } + case DOUBLE -> { + double d = (Double) value; + yield Double.isFinite(d) ? Double.toString(d) : null; + } + default -> null; + }; } private static final class CApiBuiltinDesc { @@ -169,31 +242,88 @@ 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 static String argName(int i) { - return "" + (char) ('a' + i); + private static final class CApiConstantDesc { + public final Element origin; + public final String name; + public final String value; + + public CApiConstantDesc(Element origin, String name, String value) { + this.origin = origin; + this.name = name; + this.value = value; + } + } + + 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 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(), + CApiConstant.class.getName(), CApiConstants.class.getName(), CApiStructs.class.getName(), + CApiExternalFunctionSignatures.class.getName()); } @Override @@ -359,11 +489,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 +524,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 +540,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) { @@ -474,14 +621,19 @@ private String getArgSignatureWithName(VariableElement arg, int i) { * * @throws IOException */ - private void generateCApiSource(List javaBuiltins, List constants, List fields, List structs) throws IOException { + private void generateCApiSource(List javaBuiltins, List constants, List fields, List structs, List capiSymbols) throws IOException { ArrayList lines = new ArrayList<>(); for (var entry : javaBuiltins) { String name = entry.name; CApiBuiltinDesc value = entry; if (value.call.equals("Direct") || value.call.equals("NotImplemented")) { lines.add("#undef " + name); - String line = "PyAPI_FUNC(" + getCSignature(value.returnType) + ") " + name + "("; + String line; + if (name.startsWith("GraalPyPrivate_")) { + line = "GraalPy_CAPI_HELPER_SYMBOL " + getCSignature(value.returnType) + " " + name + "("; + } else { + line = "PyAPI_FUNC(" + getCSignature(value.returnType) + ") " + name + "("; + } for (int i = 0; i < value.arguments.length; i++) { line += (i == 0 ? "" : ", ") + getArgSignatureWithName(value.arguments[i], i); } @@ -502,17 +654,11 @@ private void generateCApiSource(List javaBuiltins, List } } - lines.add("PyAPI_FUNC(int64_t*) GraalPyPrivate_Constants() {"); - lines.add(" static int64_t constants[] = {"); + lines.add("Py_EXPORTED_SYMBOL int64_t GraalPy_CAPI_METADATA[] = {"); for (var constant : constants) { lines.add(" (int64_t) " + constant + ","); } - lines.add(" 0xdead1111 // marker value"); - lines.add(" };"); - lines.add(" return constants;"); - lines.add("}"); - lines.add("PyAPI_FUNC(Py_ssize_t*) GraalPyPrivate_StructOffsets() {"); - lines.add(" static Py_ssize_t offsets[] = {"); + lines.add(" 0xdead1111, // constants marker value"); for (var field : fields) { int delim = field.indexOf("__"); assert delim != -1; @@ -521,34 +667,40 @@ private void generateCApiSource(List javaBuiltins, List name = name.replace("__", "."); // to allow inlined structs lines.add(" offsetof(" + struct + ", " + name + "),"); } - lines.add(" 0xdead2222 // marker value"); - lines.add(" };"); - lines.add(" return offsets;"); - lines.add("}"); - lines.add("PyAPI_FUNC(Py_ssize_t*) GraalPyPrivate_StructSizes() {"); - lines.add(" static Py_ssize_t sizes[] = {"); + lines.add(" 0xdead2222, // struct offsets marker value"); for (var struct : structs) { lines.add(" sizeof(" + struct.replace("__", " ") + "),"); } - lines.add(" 0xdead3333 // marker value"); + lines.add(" 0xdead3333 // struct sizes marker value"); lines.add(" };"); - lines.add(" return sizes;"); - lines.add("}"); + lines.add(""); + lines.add("Py_EXPORTED_SYMBOL void *GraalPy_CAPI_HELPERS[] = {"); + for (var helper : capiSymbols) { + lines.add(" (void *)" + helper.cName + ","); + } + lines.add("};"); updateResource("capi.gen.c.h", javaBuiltins, lines); } - private void updateResource(String name, List javaBuiltins, List lines) throws IOException { - var origins = javaBuiltins.stream().map((jb) -> jb.origin).toArray(Element[]::new); + private void updateResource(String name, List javaBuiltins, List additionalOrigins, + List lines, StandardLocation loc) + throws IOException { + var originsList = new ArrayList(); + for (var javaBuiltin : javaBuiltins) { + originsList.add(javaBuiltin.origin); + } + originsList.addAll(additionalOrigins); + var origins = originsList.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,13 +709,132 @@ private void updateResource(String name, List javaBuiltins, Lis } } + private void updateResource(String name, List javaBuiltins, List lines, + StandardLocation loc) throws IOException { + updateResource(name, javaBuiltins, List.of(), lines, loc); + } + + private void updateResource(String name, List javaBuiltins, List lines) throws IOException { + updateResource(name, javaBuiltins, lines, StandardLocation.NATIVE_HEADER_OUTPUT); + } + + private Map getArgDescriptorsByName() { + TypeElement argDescriptorType = processingEnv.getElementUtils().getTypeElement("com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor"); + if (argDescriptorType == null) { + processingEnv.getMessager().printError("Could not resolve ArgDescriptor while generating C API helper declarations."); + return Map.of(); + } + Map result = new HashMap<>(); + for (var element : argDescriptorType.getEnclosedElements()) { + if (element.getKind() == ElementKind.ENUM_CONSTANT) { + result.put(name(element), (VariableElement) element); + } + } + return result; + } + + private static final class CApiSymbolDesc { + final String cName; + final CApiExternalFunctionSignatureDesc signature; + + CApiSymbolDesc(String cName, CApiExternalFunctionSignatureDesc signature) { + this.cName = cName; + this.signature = signature; + } + } + + private List collectNativeCAPISymbols(Map externalFunctionSignatures) { + TypeElement nativeCAPISymbolType = processingEnv.getElementUtils().getTypeElement("com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol"); + if (nativeCAPISymbolType == null) { + processingEnv.getMessager().printError("Could not resolve NativeCAPISymbol while generating C API helper declarations."); + return List.of(); + } + List helpers = new ArrayList<>(); + for (var element : nativeCAPISymbolType.getEnclosedElements()) { + if (element.getKind() != ElementKind.ENUM_CONSTANT) { + continue; + } + String initializer = getNativeCAPISymbolInitializer((VariableElement) element); + String[] args = splitInitializerArgs(initializer); + if (args.length < 2) { + processingEnv.getMessager().printError("Invalid NativeCAPISymbol initializer for " + name(element), element); + continue; + } + String cName = args[0].strip().replace("\"", ""); + String signatureName = args[1].strip(); + int lastDot = signatureName.lastIndexOf('.'); + if (lastDot >= 0) { + signatureName = signatureName.substring(lastDot + 1); + } + CApiExternalFunctionSignatureDesc signature = externalFunctionSignatures.get(signatureName); + if (signature == null) { + processingEnv.getMessager().printError("Could not resolve C API helper signature " + signatureName + " for " + name(element), element); + continue; + } + helpers.add(new CApiSymbolDesc(cName, signature)); + } + return helpers; + } + + private void addCApiSymbolDeclarations(List lines, List capiSymbols) { + List declarations = new ArrayList<>(); + Map argDescriptors = getArgDescriptorsByName(); + for (var helper : capiSymbols) { + if (!helper.cName.startsWith("GraalPyPrivate_")) { + continue; + } + String externalFunctionSignatureInitializer = getExternalFunctionSignatureInitializer(helper.signature.origin); + String[] signatureArgs = splitInitializerArgs(externalFunctionSignatureInitializer); + if (signatureArgs.length < 2) { + processingEnv.getMessager().printError("Invalid C API helper signature initializer for " + helper.signature.name, helper.signature.origin); + continue; + } + VariableElement returnType = argDescriptors.get(signatureArgs[1].strip()); + if (returnType == null) { + processingEnv.getMessager().printError("Could not resolve return type " + signatureArgs[1].strip() + " for C API helper " + helper.cName, helper.signature.origin); + continue; + } + String line = "GraalPy_CAPI_HELPER_SYMBOL " + getCSignature(returnType) + " " + helper.cName + "("; + if (signatureArgs.length == 2) { + line += "void"; + } else { + boolean valid = true; + for (int i = 2; i < signatureArgs.length; i++) { + String argType = signatureArgs[i].strip(); + VariableElement arg = argDescriptors.get(argType); + if (arg == null) { + processingEnv.getMessager().printError("Could not resolve argument type " + argType + " for C API helper " + helper.cName, helper.signature.origin); + valid = false; + break; + } + line += (i == 2 ? "" : ", ") + getArgSignatureWithName(arg, i - 2); + } + if (!valid) { + continue; + } + } + line += ");"; + declarations.add(line); + } + lines.add("PyAPI_DATA(void *) GraalPy_CAPI_HELPERS[];"); + lines.add("PyAPI_DATA(int64_t) GraalPy_CAPI_METADATA[];"); + lines.add(""); + lines.addAll(declarations); + } + /** * 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 * "GraalPyPrivate_Set_" builtins. */ - private void generateCApiHeader(List javaBuiltins) throws IOException { + private void generateCApiHeader(List javaBuiltins, List javaConstants, List capiSymbols) throws IOException { List lines = new ArrayList<>(); + for (var javaConstant : javaConstants) { + lines.add("#define " + javaConstant.name + " " + javaConstant.value); + } + if (!javaConstants.isEmpty()) { + lines.add(""); + } lines.add("#define CAPI_BUILTINS \\"); int id = 0; for (var entry : javaBuiltins) { @@ -578,6 +849,9 @@ private void generateCApiHeader(List javaBuiltins) throws IOExc } lines.add(""); + addCApiSymbolDeclarations(lines, capiSymbols); + lines.add(""); + for (var entry : javaBuiltins) { String name = entry.name; if (entry.origin.getEnclosingElement().getSimpleName().toString().equals("PythonCextSlotBuiltins")) { @@ -607,7 +881,110 @@ private void generateCApiHeader(List javaBuiltins) throws IOExc } } - updateResource("capi.gen.h", javaBuiltins, lines); + updateResource("capi.gen.h", javaBuiltins, javaConstants.stream().map((c) -> c.origin).toList(), lines, + StandardLocation.NATIVE_HEADER_OUTPUT); + } + + /** + * Generates the native image config for the direct upcalls to the builtins. + */ + private void generateUpcallConfig(List javaBuiltins, List explicitUpcallTargets) 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(" {"); + emitDirectUpcall(lines, classString, methodString, capiTypeToForeignPrimitiveType(builtin.returnType), argString); + if (i < javaBuiltins.size() - 1 || !explicitUpcallTargets.isEmpty()) { + lines.add(" },"); + } else { + lines.add(" }"); + } + } + for (int i = 0; i < explicitUpcallTargets.size(); i++) { + ExecutableElement explicitUpcallTarget = explicitUpcallTargets.get(i); + + if (!verifySignatureOfExplicitUpcallTarget(explicitUpcallTarget)) { + continue; + } + + Element enclosingElement = explicitUpcallTarget.getEnclosingElement(); + if (enclosingElement.getKind() != ElementKind.CLASS) { + processingEnv.getMessager().printError("Method is expected to be enclosed by a class", explicitUpcallTarget); + continue; + } + String classString = processingEnv.getElementUtils().getBinaryName((TypeElement) enclosingElement).toString(); + String methodString = explicitUpcallTarget.getSimpleName().toString(); + String argString = explicitUpcallTarget.getParameters().stream().map(VariableElement::asType).map(t -> '"' + toJNIName(t) + '"').collect(Collectors.joining(", ")); + lines.add(" {"); + emitDirectUpcall(lines, classString, methodString, explicitUpcallTarget.getReturnType().toString(), argString); + if (i < explicitUpcallTargets.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(); + } + + private static void emitDirectUpcall(ArrayList lines, String classString, String methodString, String returnType, String argString) { + lines.add(" \"class\": \"" + classString + "\","); + lines.add(" \"method\": \"" + methodString + "\","); + lines.add(" \"returnType\": \"" + returnType + "\","); + lines.add(" \"parameterTypes\": [" + argString + "]"); + } + + private static String toJNIName(TypeMirror typeMirror) { + assert typeMirror.getKind().isPrimitive(); + if (typeMirror.getKind() == TypeKind.VOID) { + return "void"; + } + return "j" + typeMirror; + } + + private boolean verifySignatureOfExplicitUpcallTarget(ExecutableElement explicitUpcallTarget) { + TypeKind returnKind = explicitUpcallTarget.getReturnType().getKind(); + if (returnKind != TypeKind.VOID && !returnKind.isPrimitive()) { + processingEnv.getMessager().printError("Return type must be primitive but was " + explicitUpcallTarget.getReturnType(), explicitUpcallTarget); + return false; + } + + for (int i = 0; i < explicitUpcallTarget.getParameters().size(); i++) { + VariableElement variableElement = explicitUpcallTarget.getParameters().get(i); + if (!variableElement.asType().getKind().isPrimitive()) { + processingEnv.getMessager().printError("Parameter type must be primitive but was " + variableElement.asType(), variableElement); + return false; + } + } + return true; } /** @@ -616,26 +993,37 @@ private void generateCApiHeader(List javaBuiltins) throws IOExc */ 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 +1039,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); @@ -671,7 +1135,7 @@ private void generateCApiAsserts(List allBuiltins) throws IOExc lines.add(""); for (var builtin : allBuiltins) { lines.add(" hasMember = reallyHasMember(capiLibrary, \"" + builtin.name + "\");"); - if (builtin.call.equals("CImpl") || builtin.call.equals("Direct") || builtin.call.equals("NotImplemented")) { + if (builtin.call.equals("CImpl") || builtin.call.equals("NotImplemented") || (builtin.call.equals("Direct") && !builtin.name.startsWith("GraalPyPrivate_"))) { lines.add(" if (!hasMember) messages.add(\"missing implementation: " + builtin.name + "\");"); } } @@ -680,38 +1144,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 +1174,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 +1290,881 @@ 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", "INT64_T_PTR", "INTPTR_T_PTR", "SIZE_T", "UINTPTR_T", "Long", "UNSIGNED_LONG", "UINT64_T", + "PyObjectReturn", "PyObject", "PyObjectTransfer", "PyObjectConstArray", "PyObjectPtr", "PyTypeObject", "ConstPyLongObject", "PyThreadState", "PyThreadStatePtr", + "CharPtrAsTruffleString", "IterResult", "Pointer", "CHAR_PTR", "ConstCharPtr", "INT8_T_PTR", "PY_BUFFER_PTR", "PY_CAPSULE_DESTRUCTOR", "PY_SSIZE_T_PTR", "VA_LIST", + "visitproc" -> + "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 boolean isCannotRaise(VariableElement signature) { + String externalFunctionSignatureInitializer = getExternalFunctionSignatureInitializer(signature); + String[] initArgs = splitInitializerArgs(externalFunctionSignatureInitializer); + 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); + + String[] initArgs = splitInitializerArgs(externalFunctionSignatureInitializer); + + 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); + 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++)); + } + NativeDowncallMethodHandleGenerator.emitMethodHandleField(lines, NativeDowncallMethodHandleGenerator.methodHandleVarName(sig.name), returnType, argTypes); + + 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(" " + NativeDowncallMethodHandleGenerator.methodHandleVarName(sig.name) + ".invokeExact(" + directArgExpr + ");"); + } else { + lines.add(" return (" + returnType + ") " + NativeDowncallMethodHandleGenerator.methodHandleVarName(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("import " + NativeSimpleType.class.getCanonicalName() + ";"); + 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(" @Override"); + lines.add(" protected boolean isCurrentThreadVirtualImpl() {"); + lines.add(" return Thread.currentThread().isVirtual();"); + 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 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("import " + NativeSimpleType.class.getCanonicalName() + ";"); + 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(""); + lines.add(" @Override"); + lines.add(" protected boolean isCurrentThreadVirtualImpl() {"); + lines.add(" return false;"); + 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 +2186,38 @@ 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 javaConstants = new ArrayList<>(); + List externalFunctionSignatures = new ArrayList<>(); + for (var el : re.getElementsAnnotatedWith(CApiConstant.class)) { + if (el.getKind() == ElementKind.FIELD && el.asType().getKind().isPrimitive() && + el.getModifiers().contains(Modifier.STATIC) && el.getModifiers().contains(Modifier.FINAL)) { + VariableElement constant = (VariableElement) el; + Object value = constant.getConstantValue(); + String cValue = value == null ? null : formatCConstantValue(constant, value); + if (cValue != null) { + javaConstants.add(new CApiConstantDesc(constant, constant.getSimpleName().toString(), cValue)); + } else { + processingEnv.getMessager().printError(CApiConstant.class.getSimpleName() + + " is only applicable for Java primitive compile-time constants with C-representable values.", el); + } + } else { + processingEnv.getMessager().printError(CApiConstant.class.getSimpleName() + + " is only applicable for static final Java primitive fields.", el); + } + } + javaConstants.sort((a, b) -> a.name.compareTo(b.name)); + for (int i = 1; i < javaConstants.size(); i++) { + if (javaConstants.get(i - 1).name.equals(javaConstants.get(i).name)) { + processingEnv.getMessager().printError("Duplicate " + CApiConstant.class.getSimpleName() + + " name: " + javaConstants.get(i).name, + javaConstants.get(i).origin); + } + } for (var el : re.getElementsAnnotatedWith(CApiConstants.class)) { if (el.getKind() == ElementKind.ENUM) { for (var enumBit : el.getEnclosedElements()) { @@ -874,7 +2231,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 +2242,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 +2251,46 @@ public boolean process(Set annotations, RoundEnvironment } } } + List explicitUpcallTargets = new LinkedList<>(); + for (var el : re.getElementsAnnotatedWith(CApiUpcallTarget.class)) { + if (el.getKind() == ElementKind.METHOD) { + explicitUpcallTargets.add((ExecutableElement) el); + } else { + processingEnv.getMessager().printError(CApiUpcallTarget.class.getSimpleName() + " is only applicable for methods.", el); + } + } + 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); + List capiSymbols = collectNativeCAPISymbols(sigs); + generateCApiSource(allBuiltins, constants, fields, structs, capiSymbols); + generateCApiHeader(javaBuiltins, javaConstants, capiSymbols); + generateExternalFunctionInvoker(new ArrayList<>(sigs.values())); + generateExternalFunctionHelperNodes(externalFunctionDescs); + generateExternalFunctionRootNodes(externalFunctionDescs, cApiExternalFunctionWrapperDescs, sigs); } generateBuiltinRegistry(javaBuiltins); + generateUpcallConfig(javaBuiltins, explicitUpcallTargets); generateCApiAsserts(allBuiltins); if (trees != null) { // needs jdk.compiler diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java new file mode 100644 index 0000000000..ede35d6f5f --- /dev/null +++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java @@ -0,0 +1,325 @@ +/* + * 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.processor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic.Kind; + +import com.oracle.graal.python.annotations.DowncallSignature; +import com.oracle.graal.python.annotations.NativeSimpleType; + +public class GenerateNativeDowncallsProcessor extends AbstractProcessor { + private record NativeDowncallDesc(String name, String symbolName, NativeSimpleType returnType, List argumentTypes, List argumentNames) { + } + + @Override + public Set getSupportedAnnotationTypes() { + return Set.of(DowncallSignature.class.getName()); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return true; + } + try { + doProcess(roundEnv); + } catch (IOException ex) { + ex.printStackTrace(); + } catch (ProcessingError ex) { + processingEnv.getMessager().printMessage(Kind.ERROR, ex.getMessage(), ex.getElement()); + } + return true; + } + + private void doProcess(RoundEnvironment roundEnv) throws IOException, ProcessingError { + Set invokerElements = new LinkedHashSet<>(); + for (Element element : roundEnv.getElementsAnnotatedWith(DowncallSignature.class)) { + invokerElements.add(validateDowncallSignature(element)); + } + for (TypeElement invokerElement : invokerElements) { + generateInvoker(invokerElement); + } + } + + private static TypeElement validateDowncallSignature(Element element) throws ProcessingError { + if (element.getKind() != ElementKind.METHOD) { + throw error(element, "@DowncallSignature can only annotate methods"); + } + Element enclosingElement = element.getEnclosingElement(); + if (enclosingElement == null || enclosingElement.getKind() != ElementKind.CLASS) { + throw error(element, "@DowncallSignature can only annotate methods in classes"); + } + if (!enclosingElement.getModifiers().contains(Modifier.ABSTRACT)) { + throw error(enclosingElement, "@DowncallSignature methods must be enclosed in an abstract class"); + } + if (!element.getModifiers().contains(Modifier.ABSTRACT)) { + throw error(element, "@DowncallSignature methods must be abstract"); + } + return (TypeElement) enclosingElement; + } + + private void generateInvoker(TypeElement invokerElement) throws IOException, ProcessingError { + List downcalls = collectDowncalls(invokerElement); + if (downcalls.isEmpty()) { + throw error(invokerElement, "Annotated class does not declare any downcalls"); + } + + String packageName = processingEnv.getElementUtils().getPackageOf(invokerElement).getQualifiedName().toString(); + String invokerQualifiedName = invokerElement.getQualifiedName().toString(); + String invokerTypeRef = invokerQualifiedName.startsWith(packageName + ".") ? invokerQualifiedName.substring(packageName.length() + 1) : invokerQualifiedName; + String className = invokerElement.getSimpleName() + "Gen"; + + 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 java.lang.invoke.MethodHandle;"); + lines.add("import java.lang.invoke.MethodType;"); + lines.add("import java.util.concurrent.atomic.AtomicLongArray;"); + lines.add(""); + lines.add("import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport;"); + lines.add("import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary;"); + lines.add("import com.oracle.truffle.api.CompilerDirectives;"); + lines.add("import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;"); + lines.add(""); + lines.add("final class " + className + " extends " + invokerTypeRef + " {"); + lines.add(" private final PythonContext context;"); + lines.add(" private final AtomicLongArray cachedFunctions = new AtomicLongArray(" + downcalls.size() + ");"); + lines.add(" private volatile NativeLibrary nativeLibrary;"); + lines.add(""); + + for (NativeDowncallDesc downcall : downcalls) { + NativeDowncallMethodHandleGenerator.emitMethodHandleField(lines, methodHandleName(downcall.name), downcall.returnType, downcall.argumentTypes); + } + + lines.add(""); + lines.add(" " + className + "(PythonContext context) {"); + lines.add(" this.context = context;"); + lines.add(" }"); + + for (int i = 0; i < downcalls.size(); i++) { + emitDowncallMethod(lines, downcalls.get(i), i); + } + + lines.add(""); + lines.add(" @TruffleBoundary"); + lines.add(" private long lookup(int functionIndex, String symbolName) {"); + lines.add(" long symbol = cachedFunctions.get(functionIndex);"); + lines.add(" if (symbol == 0) {"); + lines.add(" symbol = loadFunction(symbolName);"); + lines.add(" cachedFunctions.compareAndSet(functionIndex, 0, symbol);"); + lines.add(" symbol = cachedFunctions.get(functionIndex);"); + lines.add(" }"); + lines.add(" return symbol;"); + lines.add(" }"); + lines.add(""); + lines.add(" @TruffleBoundary"); + lines.add(" private long loadFunction(String symbolName) {"); + lines.add(" return ensureLibrary().lookupSymbol(symbolName);"); + lines.add(" }"); + lines.add(""); + lines.add(" @TruffleBoundary"); + lines.add(" private NativeLibrary ensureLibrary() {"); + lines.add(" NativeLibrary library = nativeLibrary;"); + lines.add(" if (library == null) {"); + lines.add(" library = " + invokerTypeRef + ".loadNativeLibrary(context);"); + lines.add(" nativeLibrary = library;"); + lines.add(" }"); + lines.add(" return library;"); + lines.add(" }"); + lines.add("}"); + + var file = processingEnv.getFiler().createSourceFile(packageName + "." + className, invokerElement); + try (var writer = file.openWriter()) { + writer.append(String.join(System.lineSeparator(), lines)); + } + } + + private static List collectDowncalls(TypeElement invokerElement) throws ProcessingError { + List result = new ArrayList<>(); + Set methodNames = new java.util.HashSet<>(); + for (Element enclosedElement : invokerElement.getEnclosedElements()) { + if (enclosedElement.getKind() == ElementKind.METHOD && enclosedElement.getAnnotation(DowncallSignature.class) != null) { + NativeDowncallDesc downcall = extractDowncall((ExecutableElement) enclosedElement); + if (!methodNames.add(downcall.name)) { + throw error(enclosedElement, "Duplicate downcall method name: %s", downcall.name); + } + result.add(downcall); + } + } + return result; + } + + private static NativeDowncallDesc extractDowncall(ExecutableElement method) throws ProcessingError { + DowncallSignature annotation = method.getAnnotation(DowncallSignature.class); + if (annotation == null) { + throw error(method, "Downcall method must be annotated with @DowncallSignature"); + } + + NativeSimpleType[] argTypes = annotation.argumentTypes(); + if (argTypes.length != method.getParameters().size()) { + throw error(method, "@DowncallSignature argumentTypes length must match method parameter count (%d != %d)", argTypes.length, method.getParameters().size()); + } + validateJavaType(method, method.getReturnType(), annotation.returnType()); + for (int i = 0; i < argTypes.length; i++) { + validateJavaType(method.getParameters().get(i), method.getParameters().get(i).asType(), argTypes[i]); + } + + List argumentTypes = List.of(argTypes); + List argumentNames = extractArgumentNames(method); + String symbolName = method.getSimpleName().toString(); + return new NativeDowncallDesc( + symbolName, + symbolName, + annotation.returnType(), + argumentTypes, + argumentNames); + } + + private static List extractArgumentNames(ExecutableElement method) throws ProcessingError { + List result = new ArrayList<>(method.getParameters().size()); + for (VariableElement parameter : method.getParameters()) { + String argName = parameter.getSimpleName().toString(); + if (argName.isBlank()) { + throw error(parameter, "Downcall parameter name must not be blank"); + } + if (!SourceVersion.isIdentifier(argName) || SourceVersion.isKeyword(argName)) { + throw error(parameter, "Downcall parameter name is not a valid Java identifier: %s", argName); + } + result.add(argName); + } + return result; + } + + private static void validateJavaType(Element element, TypeMirror actualType, NativeSimpleType nativeType) throws ProcessingError { + TypeKind expected = switch (nativeType) { + case VOID -> TypeKind.VOID; + case SINT8 -> TypeKind.BYTE; + case SINT16 -> TypeKind.SHORT; + case SINT32 -> TypeKind.INT; + case SINT64, POINTER -> TypeKind.LONG; + case FLOAT -> TypeKind.FLOAT; + case DOUBLE -> TypeKind.DOUBLE; + }; + if (actualType.getKind() != expected) { + throw error(element, "Java type %s does not match native type %s", actualType, nativeType); + } + } + + private static ProcessingError error(Element element, String fmt, Object... args) throws ProcessingError { + throw new ProcessingError(element, fmt, args); + } + + private static String nativeSimpleTypeToJavaType(NativeSimpleType type) { + return switch (type) { + case VOID -> "void"; + case SINT8 -> "byte"; + case SINT16 -> "short"; + case SINT32 -> "int"; + case SINT64, POINTER -> "long"; + case FLOAT -> "float"; + case DOUBLE -> "double"; + }; + } + + private static void emitDowncallMethod(List lines, NativeDowncallDesc downcall, int functionIndex) { + lines.add(""); + lines.add(" @TruffleBoundary(allowInlining = true, transferToInterpreterOnException = false)"); + lines.add(" @Override"); + lines.add(" " + nativeSimpleTypeToJavaType(downcall.returnType) + " " + downcall.name + "(" + typedArgs(downcall.argumentTypes, downcall.argumentNames) + ") {"); + lines.add(" long functionPointer = lookup(" + functionIndex + ", " + stringLiteral(downcall.symbolName) + ");"); + lines.add(" try {"); + if (NativeSimpleType.VOID == downcall.returnType) { + lines.add(" " + methodHandleName(downcall.name) + ".invokeExact(" + invokeArgs(downcall.argumentNames) + ");"); + } else { + lines.add(" return (" + nativeSimpleTypeToJavaType(downcall.returnType) + ") " + methodHandleName(downcall.name) + ".invokeExact(" + invokeArgs(downcall.argumentNames) + ");"); + } + lines.add(" } catch (Throwable t) {"); + lines.add(" throw CompilerDirectives.shouldNotReachHere(t);"); + lines.add(" }"); + lines.add(" }"); + } + + private static String methodHandleName(String downcallName) { + return NativeDowncallMethodHandleGenerator.methodHandleVarName(downcallName.toUpperCase()); + } + + private static String typedArgs(List argTypes, List argNames) { + List args = new ArrayList<>(argTypes.size()); + for (int i = 0; i < argTypes.size(); i++) { + args.add(nativeSimpleTypeToJavaType(argTypes.get(i)) + " " + argNames.get(i)); + } + return String.join(", ", args); + } + + private static String invokeArgs(List argNames) { + String args = String.join(", ", argNames); + return args.isEmpty() ? "functionPointer" : "functionPointer, " + args; + } + + private static String stringLiteral(String value) { + return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\""; + } +} diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java new file mode 100644 index 0000000000..e31e059c70 --- /dev/null +++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java @@ -0,0 +1,107 @@ +/* + * 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.processor; + +import java.util.ArrayList; +import java.util.List; + +import com.oracle.graal.python.annotations.NativeSimpleType; + +final class NativeDowncallMethodHandleGenerator { + private NativeDowncallMethodHandleGenerator() { + } + + static String argName(int i) { + if (i >= 26) { + throw new IllegalArgumentException("Generated downcall stubs support at most 26 synthetic argument names (a-z), got index " + i); + } + return "" + (char) ('a' + i); + } + + static String methodHandleVarName(String signatureName) { + return "NATIVE_METHOD_HANDLE_" + signatureName; + } + + static String toClassLiteral(String javaType) { + return switch (javaType) { + case "void" -> "void.class"; + case "byte" -> "byte.class"; + case "short" -> "short.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); + }; + } + + static String toClassLiteral(NativeSimpleType nativeType) { + return switch (nativeType) { + case VOID -> "void.class"; + case SINT8 -> "byte.class"; + case SINT16 -> "short.class"; + case SINT32 -> "int.class"; + case SINT64, POINTER -> "long.class"; + case FLOAT -> "float.class"; + case DOUBLE -> "double.class"; + }; + } + + static void emitMethodHandleField(List lines, String fieldName, String returnType, List argTypes) { + List methodTypeArgs = new ArrayList<>(); + methodTypeArgs.add("long.class"); + for (String argType : argTypes) { + methodTypeArgs.add(toClassLiteral(argType)); + } + lines.add(" private static final MethodHandle " + fieldName + " = NativeAccessSupport.createDowncallHandle(" + + "MethodType.methodType(" + toClassLiteral(returnType) + ", " + String.join(", ", methodTypeArgs) + "), false);"); + } + + static void emitMethodHandleField(List lines, String fieldName, NativeSimpleType returnType, List argTypes) { + List methodTypeArgs = new ArrayList<>(); + methodTypeArgs.add("long.class"); + for (NativeSimpleType argType : argTypes) { + methodTypeArgs.add(toClassLiteral(argType)); + } + lines.add(" private static final MethodHandle " + fieldName + " = NativeAccessSupport.createDowncallHandle(" + + "MethodType.methodType(" + toClassLiteral(returnType) + ", " + String.join(", ", methodTypeArgs) + "), false);"); + } +} diff --git a/graalpython/com.oracle.graal.python.resources/pom.xml b/graalpython/com.oracle.graal.python.resources/pom.xml new file mode 100644 index 0000000000..ce411c3713 --- /dev/null +++ b/graalpython/com.oracle.graal.python.resources/pom.xml @@ -0,0 +1,69 @@ + + + + 4.0.0 + + + org.graalvm.python.ide + graalpy-source-workspace + 1.0-SNAPSHOT + ../../pom.xml + + + com.oracle.graal.python.resources + jar + + + + org.graalvm.truffle + truffle-api + + + org.graalvm.truffle + truffle-dsl-processor + provided + true + + + diff --git a/graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java b/graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java index 3894c908ce..4b76ac03e2 100644 --- a/graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java +++ b/graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java @@ -43,6 +43,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.ArrayList; @@ -66,6 +67,7 @@ public final class PythonResource implements InternalResource { private static final int PYTHON_MINOR; private static final int GRAALVM_MAJOR; private static final int GRAALVM_MINOR; + private static final String PYTHON_ABIFLAGS; /** * The version generated at build time is stored in an ASCII-compatible way. Add build time, we @@ -82,6 +84,13 @@ public final class PythonResource implements InternalResource { is.read(); // skip python micro version GRAALVM_MAJOR = is.read() - VERSION_BASE; GRAALVM_MINOR = is.read() - VERSION_BASE; + is.read(); // skip GraalVM micro version + is.read(); // skip release level + int ch; + while ((ch = is.read()) != '\n' && ch != -1) { + // skip ABI version + } + PYTHON_ABIFLAGS = ch == -1 ? "" : new String(is.readAllBytes(), StandardCharsets.US_ASCII).strip(); } catch (IOException e) { throw new RuntimeException(e); } @@ -117,7 +126,7 @@ public void unpackFiles(Env env, Path targetDirectory) throws IOException { env.unpackResourceFiles(BASE_PATH.resolve(LIBPYTHON_FILES), targetDirectory.resolve("lib").resolve(pythonMajMin), BASE_PATH.resolve(LIBPYTHON), filter); env.unpackResourceFiles(BASE_PATH.resolve(LIBGRAALPY_FILES), targetDirectory.resolve("lib").resolve("graalpy" + GRAALVM_MAJOR + "." + GRAALVM_MINOR), BASE_PATH.resolve(LIBGRAALPY), filter); - env.unpackResourceFiles(BASE_PATH.resolve(INCLUDE_FILES), targetDirectory.resolve("include").resolve(pythonMajMin), BASE_PATH.resolve(INCLUDE), filter); + env.unpackResourceFiles(BASE_PATH.resolve(INCLUDE_FILES), targetDirectory.resolve("include").resolve(pythonMajMin + PYTHON_ABIFLAGS), BASE_PATH.resolve(INCLUDE), filter); } // ni files are in the same place on all platforms env.unpackResourceFiles(BASE_PATH.resolve(NI_FILES), targetDirectory, BASE_PATH, filter); diff --git a/graalpython/com.oracle.graal.python.shell/pom.xml b/graalpython/com.oracle.graal.python.shell/pom.xml new file mode 100644 index 0000000000..c086fee5f2 --- /dev/null +++ b/graalpython/com.oracle.graal.python.shell/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + + org.graalvm.python.ide + graalpy-source-workspace + 1.0-SNAPSHOT + ../../pom.xml + + + com.oracle.graal.python.shell + jar + + + + org.graalvm.polyglot + polyglot + + + org.graalvm.sdk + launcher-common + + + org.graalvm.shadowed + jline + + + org.graalvm.sdk + maven-downloader + + + 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 cf23a4034b..5f2ec5e99f 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 @@ -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. @@ -34,6 +34,12 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.management.ManagementFactory; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.NoSuchFileException; @@ -80,6 +86,56 @@ public static void main(String[] args) { new GraalPythonMain().launch(args); } + @Override + protected String decodeArgument(byte[] argument) { + Charset charset = Charset.defaultCharset(); + String decoded = new String(argument, charset); + if (decoded.indexOf('\uFFFD') < 0) { + return decoded; + } + return decodeArgument(argument, charset); + } + + /* + * Match documented Unix sys.argv behavior: + * https://docs.python.org/3.12/library/sys.html#sys.argv + * "When you need original bytes, you can get it by `[os.fsencode(arg) for arg in sys.argv]`." + */ + private static String decodeArgument(byte[] argument, Charset charset) { + CharsetDecoder decoder = charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT); + StringBuilder builder = new StringBuilder(argument.length); + ByteBuffer in = ByteBuffer.wrap(argument); + CharBuffer out = CharBuffer.allocate(Math.max(1, (int) Math.ceil(argument.length * decoder.maxCharsPerByte()))); + while (true) { + CoderResult result = decoder.decode(in, out, true); + appendDecodedChars(builder, out); + if (result.isUnderflow()) { + break; + } else if (result.isOverflow()) { + continue; + } else if (result.isError()) { + int errorLength = result.length(); + for (int i = 0; i < errorLength && in.hasRemaining(); i++) { + builder.append((char) (0xDC00 + (in.get() & 0xff))); + } + } + } + while (true) { + CoderResult result = decoder.flush(out); + appendDecodedChars(builder, out); + if (result.isUnderflow()) { + break; + } + } + return builder.toString(); + } + + private static void appendDecodedChars(StringBuilder builder, CharBuffer out) { + out.flip(); + builder.append(out); + out.clear(); + } + private static final String LANGUAGE_ID = "python"; private static final String J_PYENVCFG = "pyvenv.cfg"; @@ -102,6 +158,7 @@ public static void main(String[] args) { private boolean noSite = false; private boolean unbufferedIO = false; private boolean multiContext = false; + private int repeatedRuns = 1; private boolean snaptshotStartup = false; private boolean warnDefaultEncoding = false; private int intMaxStrDigits = -1; @@ -182,8 +239,10 @@ protected List preprocessArguments(List givenArgs, Map argumentIterator = arguments.iterator(); argumentIterator.hasNext();) { String arg = argumentIterator.next(); origArgs.add(arg); @@ -229,6 +288,9 @@ protected List preprocessArguments(List givenArgs, Map preprocessArguments(List givenArgs, Map preprocessArguments(List givenArgs, Map preprocessArguments(List givenArgs, Map 0) { intMaxStrDigits = validateIntMaxStrDigits(xOption.substring(eq), "-X int_max_str_digits"); } + } else if ("jit".equals(xOption) || xOption.startsWith("jit=")) { + applyJitModePreset(polyglotOptions, xOption); + jitModePresetSpecified = true; } break shortOptionLoop; default: @@ -451,12 +521,18 @@ protected List preprocessArguments(List givenArgs, Map= %d or 0 for unlimited.", name, INT_MAX_STR_DIGITS_THRESHOLD), 1); } + private void applyJitModePreset(Map polyglotOptions, String xOption) { + boolean fallbackRuntime = usesFallbackRuntime(); + switch (xOption) { + case "jit=0": + if (!fallbackRuntime) { + polyglotOptions.put("engine.Compilation", "false"); + } + break; + case "jit=1": + if (!fallbackRuntime) { + applyLatencyJitPreset(polyglotOptions); + } + break; + case "jit=2": + if (!fallbackRuntime) { + applyThroughputJitPreset(polyglotOptions); + } + break; + default: + throw abort("Invalid argument for the -X jit option: expected jit=0, jit=1, or jit=2\n" + SHORT_HELP, 2); + } + } + + private boolean usesFallbackRuntime() { + return findOptionDescriptor("engine", "engine.Compilation") == null; + } + + private static void applyLatencyJitPreset(Map polyglotOptions) { + polyglotOptions.put("engine.CompilerThreads", "1"); + applyRaisedJitThresholds(polyglotOptions); + } + + private static void applyThroughputJitPreset(Map polyglotOptions) { + polyglotOptions.put("engine.Mode", "throughput"); + applyRaisedJitThresholds(polyglotOptions); + } + + private static void applyRaisedJitThresholds(Map polyglotOptions) { + polyglotOptions.put("engine.FirstTierCompilationThreshold", "10000"); + polyglotOptions.put("engine.LastTierCompilationThreshold", "100000"); + polyglotOptions.put("engine.OSRCompilationThreshold", "200704"); + polyglotOptions.put("engine.SingleTierCompilationThreshold", "100000"); + } + private static String toAbsolutePath(String executable) { if (executable.contains(":")) { // this is either already an absolute windows path, or not a single executable @@ -1001,19 +1126,13 @@ private void findAndApplyVenvCfg(Builder contextBuilder, String executable) { } break; case "venvlauncher_command": - if (!hasContextOptionSetViaCommandLine("VenvlauncherCommand")) { - contextBuilder.option("python.VenvlauncherCommand", parts[1].trim()); - } + setOptionIfNotSetViaCommandLine(contextBuilder, "VenvlauncherCommand", parts[1].trim()); break; case "base-prefix": - if (!hasContextOptionSetViaCommandLine("SysBasePrefix")) { - contextBuilder.option("python.SysBasePrefix", parts[1].trim()); - } + setOptionIfNotSetViaCommandLine(contextBuilder, "SysBasePrefix", parts[1].trim()); break; case "base-executable": - if (!hasContextOptionSetViaCommandLine("BaseExecutable")) { - contextBuilder.option("python.BaseExecutable", parts[1].trim()); - } + setOptionIfNotSetViaCommandLine(contextBuilder, "BaseExecutable", parts[1].trim()); break; } } @@ -1044,6 +1163,12 @@ private String getContextOptionIfSetViaCommandLine(String key) { return null; } + private void setOptionIfNotSetViaCommandLine(Context.Builder builder, String key, String value) { + if (!hasContextOptionSetViaCommandLine(key)) { + builder.option("python." + key, value); + } + } + private boolean hasContextOptionSetViaCommandLine(String key) { if (System.getProperty("polyglot.python." + key) != null) { return System.getProperty("polyglot.python." + key) != null; @@ -1108,7 +1233,9 @@ protected void printHelp(OptionCategory maxCategory) { " can be supplied multiple times to increase verbosity\n" + "-V : print the Python version number and exit (also --version)\n" + " when given twice, print more information about the build\n" + - "-X opt : CPython implementation-specific options. warn_default_encoding and int_max_str_digits are supported on GraalPy\n" + + "-X opt : set implementation-specific option\n" + + " CPython-compatible options supported by GraalPy: warn_default_encoding, int_max_str_digits\n" + + " GraalPy implementation-specific options: jit=0|1|2 (default: jit=1)\n" + "-W arg : warning control; arg is action:message:category:module:lineno\n" + " also PYTHONWARNINGS=arg\n" + "file : program read from script file\n" + @@ -1209,6 +1336,7 @@ private int readEvalPrint(Context context, ConsoleHandler consoleHandler, Value while (true) { // processing subsequent lines while input is incomplete try { context.eval(Source.newBuilder(getLanguageId(), sb.toString(), "").interactive(true).buildLiteral()); + flushInteractiveOutput(sysModule); } catch (PolyglotException e) { if (ps2 == null) { ps2 = doEcho ? sysModule.getMember("ps2").asString() : null; @@ -1254,6 +1382,7 @@ private int readEvalPrint(Context context, ConsoleHandler consoleHandler, Value continue; } } + flushInteractiveOutput(sysModule); // process the exception from eval or from the last parsing of the input // + additional source if (e.isExit()) { @@ -1295,6 +1424,22 @@ private int readEvalPrint(Context context, ConsoleHandler consoleHandler, Value } } + private static void flushInteractiveOutput(Value sysModule) { + flushInteractiveStream(sysModule, "stderr"); + flushInteractiveStream(sysModule, "stdout"); + } + + private static void flushInteractiveStream(Value sysModule, String name) { + try { + Value stream = sysModule.getMember(name); + if (stream != null && !stream.isNull() && stream.canInvokeMember("flush")) { + stream.invokeMember("flush"); + } + } catch (PolyglotException | UnsupportedOperationException e) { + // Match CPython's interactive flush_io: stream flush failures are ignored. + } + } + private static boolean canSkipFromEval(String input) { String[] split = input.split("\n"); for (String s : split) { diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/EngineOptionsTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/EngineOptionsTests.java index 1f9824edae..b547ff5300 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/EngineOptionsTests.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/EngineOptionsTests.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,6 +41,7 @@ package com.oracle.graal.python.test.integration; import static com.oracle.graal.python.test.integration.Utils.IS_WINDOWS; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeFalse; @@ -57,7 +58,9 @@ public void engineOptions() { assertEquals("java", doit(engine, null)); assertEquals("java", doit(engine, "java")); - assertEquals("native", doit(engine, "native")); + // Native POSIX requires native access downcalls. When unavailable on JDK < 22 in JVM mode, + // requesting the native backend intentionally falls back to Java. + assertEquals(SUPPORTS_PANAMA ? "native" : "java", doit(engine, "native")); engine.close(); } diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/Utils.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/Utils.java index e47f5fa149..591dbc8015 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/Utils.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/Utils.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 @@ public class Utils { public static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("windows"); + public static final boolean SUPPORTS_PANAMA = Runtime.version().feature() >= 22; public static String getThreadDump() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/AsyncActionThreadingTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/AsyncActionThreadingTest.java index 2329920f60..ca05802955 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/AsyncActionThreadingTest.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/AsyncActionThreadingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, 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 @@ -40,10 +40,12 @@ */ package com.oracle.graal.python.test.integration.advanced; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Arrays; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -76,8 +78,9 @@ Stream pythonThreads() { @Test public void testNoNewThreadsWithoutAutomaticAsyncActions() { Assume.assumeTrue("false".equalsIgnoreCase(System.getProperty("python.AutomaticAsyncActions"))); + Assume.assumeTrue("Requires JEP 454 support for _testcapi", SUPPORTS_PANAMA); long threadCount = pythonThreadCount(); - Context c = PythonTests.enterContext(); + Context c = PythonTests.enterContext(Map.of("python.AllowSignalHandlers", "true", "python.PosixModuleBackend", "native"), new String[0]); try { Runnable poll = c.getPolyglotBindings().getMember("PollPythonAsyncActions").asHostObject(); c.eval("python", "import re, itertools, functools, _testcapi"); diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/BenchmarkTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/BenchmarkTests.java index 642b709f2a..d1dbbb43fc 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/BenchmarkTests.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/BenchmarkTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, 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. @@ -43,6 +43,12 @@ public void richards3() { assertBenchNoError(name, new String[]{name, "20"}); } + @Test + public void gr68715_collectOuterStackLocals() { + String name = "gr68715_collect_outer_stack_locals.py"; + assertBenchNoError(name, new String[]{name, "1000", "4", "4"}); + } + @Test public void bm_ai() { String name = "ai-nqueen.py"; 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..548fbb3545 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 @@ -40,11 +40,17 @@ */ package com.oracle.graal.python.test.integration.advanced; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.CountDownLatch; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Engine; import org.graalvm.polyglot.PolyglotException; +import org.graalvm.polyglot.Value; import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; @@ -55,9 +61,12 @@ * because we cannot create multiple contexts that would load native extensions. */ public class NativeExtTest { + private static final String DELVEWHEEL_VERSION = "1.13.0"; + @BeforeClass public static void setUpClass() { Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("mac")); + Assume.assumeTrue(SUPPORTS_PANAMA); } @Test @@ -108,6 +117,52 @@ public void testSharingErrorWithCpythonSre() throws InterruptedException { } } + @Test + public void testMissingDelvewheelError() throws IOException { + Assume.assumeTrue(System.getProperty("os.name").toLowerCase().contains("win")); + + Path tempDir = Files.createTempDirectory("graalpy-no-delvewheel"); + try (Engine engine = Engine.create("python"); + Context context = newContext(engine).option("python.IsolateNativeModules", "true").option("python.Executable", tempDir.resolve("python.exe").toString()).environment("PATH", + tempDir.toString()).build()) { + try { + context.eval("python", "import _sqlite3"); + Assert.fail("importing _sqlite3 with python.IsolateNativeModules=true should fail when delvewheel is not on PATH"); + } catch (PolyglotException ex) { + Assert.assertTrue(ex.getMessage(), ex.isGuestException()); + Value exception = ex.getGuestObject(); + Assert.assertTrue(exception.isException()); + Assert.assertEquals(ex.getMessage(), "SystemError", exception.getMetaObject().getMetaSimpleName()); + Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("delvewheel>=" + DELVEWHEEL_VERSION)); + } + } finally { + Files.deleteIfExists(tempDir); + } + } + + @Test + public void testMissingPatchelfError() throws IOException { + Assume.assumeTrue(System.getProperty("os.name").toLowerCase().contains("linux")); + + Path tempDir = Files.createTempDirectory("graalpy-no-patchelf"); + try (Engine engine = Engine.create("python"); + Context context = newContext(engine).option("python.IsolateNativeModules", "true").option("python.Executable", tempDir.resolve("python").toString()).environment("PATH", + tempDir.toString()).build()) { + try { + context.eval("python", "import _sqlite3"); + Assert.fail("importing _sqlite3 with python.IsolateNativeModules=true should fail when patchelf is not on PATH"); + } catch (PolyglotException ex) { + Assert.assertTrue(ex.getMessage(), ex.isGuestException()); + Value exception = ex.getGuestObject(); + Assert.assertTrue(exception.isException()); + Assert.assertEquals(ex.getMessage(), "SystemError", exception.getMetaObject().getMetaSimpleName()); + Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("patchelf`")); + } + } finally { + Files.deleteIfExists(tempDir); + } + } + private static Context.Builder newContext(Engine engine) { return Context.newBuilder().allowExperimentalOptions(true).allowAllAccess(true).engine(engine); } diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/ResourcesTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/ResourcesTest.java index c2ba25acac..241f674cc5 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/ResourcesTest.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/ResourcesTest.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 @@ -43,6 +43,9 @@ import static org.junit.Assert.assertTrue; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Engine; @@ -79,4 +82,33 @@ public void testResourcesAlwaysAllowReading() { assertTrue(foundHome, foundHome.contains("python" + File.separator + "python-home")); } } + + @Test + public void testTracebackSourceLineRespectsDeniedIO() throws IOException { + Path sourceFile = Files.createTempFile("graalpy-traceback-source", ".py"); + String leakedLine = "this line must not appear in the traceback"; + Files.writeString(sourceFile, leakedLine + "\n"); + try (Context context = Context.newBuilder("python").allowIO(IOAccess.NONE).build()) { + String traceback = context.eval("python", """ + import io + import traceback + + code = compile("1/0\\n", '%s', "exec") + out = io.StringIO() + try: + exec(code) + except ZeroDivisionError: + traceback.print_exc(file=out) + out.getvalue() + """.formatted(escapePythonString(sourceFile.toString()))).asString(); + assertTrue(traceback, traceback.contains(sourceFile.toString())); + assertTrue(traceback, !traceback.contains(leakedLine)); + } finally { + Files.deleteIfExists(sourceFile); + } + } + + private static String escapePythonString(String value) { + return value.replace("\\", "\\\\").replace("'", "\\'"); + } } diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/SandboxPathTraversalTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/SandboxPathTraversalTest.java new file mode 100644 index 0000000000..71e938e108 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/SandboxPathTraversalTest.java @@ -0,0 +1,214 @@ +/* + * 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.test.integration.advanced; + +import static com.oracle.graal.python.test.integration.Utils.IS_WINDOWS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.io.IOAccess; +import org.junit.Test; + +public class SandboxPathTraversalTest { + private static final String MARKER = "sandbox-path-traversal-marker"; + private static final String LEAKED_PREFIX = "LEAKED:"; + private static final String POSIX_BACKEND = "java"; + + @Test + public void cannotListHostDirectoryViaLanguageHomeDotDotPath() throws IOException { + assumeFalse(IS_WINDOWS); + Path directory = Files.createTempDirectory("graalpy-sandbox-list"); + Path markerFile = Files.writeString(directory.resolve("marker.txt"), MARKER, StandardCharsets.UTF_8); + try { + String source = listDirectoryScript(pythonStringLiteral(directory.toRealPath().toString())); + String unrestrictedResult = eval(source, IOAccess.ALL); + assertTrue(unrestrictedResult, unrestrictedResult.contains(markerFile.getFileName().toString())); + + String result = eval(source, IOAccess.NONE); + assertFalse(result, result.startsWith(LEAKED_PREFIX)); + } finally { + Files.deleteIfExists(markerFile); + Files.deleteIfExists(directory); + } + } + + @Test + public void cannotReadHostPyFileViaLanguageHomeDotDotPath() throws IOException { + assumeFalse(IS_WINDOWS); + Path directory = Files.createTempDirectory("graalpy-sandbox-read"); + Path secretFile = Files.writeString(directory.resolve("secret.py"), MARKER, StandardCharsets.UTF_8); + try { + String source = readFileScript(pythonStringLiteral(secretFile.toRealPath().toString())); + assertEquals(LEAKED_PREFIX + MARKER, eval(source, IOAccess.ALL)); + + String result = eval(source, IOAccess.NONE); + assertFalse(result, result.startsWith(LEAKED_PREFIX)); + } finally { + Files.deleteIfExists(secretFile); + Files.deleteIfExists(directory); + } + } + + @Test + public void cannotReadHostFileViaTracebackSourceLineRendering() throws IOException { + assumeFalse(IS_WINDOWS); + Path directory = Files.createTempDirectory("graalpy-sandbox-traceback"); + String contents = MARKER + "\n" + MARKER + "-second\n"; + Path secretFile = Files.writeString(directory.resolve("secret"), contents, StandardCharsets.UTF_8); + try { + String source = tracebackSourceLineScript(pythonStringLiteral(secretFile.toRealPath().toString())); + assertEquals(LEAKED_PREFIX + MARKER + "\n" + MARKER + "-second", eval(source, IOAccess.ALL)); + + String result = eval(source, IOAccess.NONE); + assertFalse(result, result.startsWith(LEAKED_PREFIX)); + assertFalse(result, result.contains(MARKER)); + } finally { + Files.deleteIfExists(secretFile); + Files.deleteIfExists(directory); + } + } + + private static String eval(String source, IOAccess ioAccess) { + try (Context context = Context.newBuilder("python").allowIO(ioAccess).option("python.PosixModuleBackend", POSIX_BACKEND).build()) { + assertEquals(POSIX_BACKEND, context.eval("python", "__graalpython__.posix_module_backend()").asString()); + return context.eval("python", source).asString(); + } + } + + private static String listDirectoryScript(String directory) { + return """ + %s + import os + + # Build a host directory path prefixed with the language home and followed + # by enough '..' components to escape it again lexically. + escaped = escaped_path(%s) + try: + # A successful result would mean os.listdir reached the host directory + # through getPublicTruffleFileRelaxed despite restricted IO. + result = "LEAKED:" + ",".join(sorted(os.listdir(escaped))) + except BaseException as e: + result = "BLOCKED:" + type(e).__name__ + result + """.formatted(escapedPathFunction(), directory); + } + + private static String readFileScript(String file) { + return """ + %s + # The target file is deliberately named secret.py to exercise the relaxed + # guard's allowed-suffix branch. + escaped = escaped_path(%s) + try: + # A successful result would mean open() reached the host file through + # the internal TruffleFile returned by getPublicTruffleFileRelaxed. + with open(escaped, "r", encoding="utf-8") as f: + result = "LEAKED:" + f.read() + except BaseException as e: + result = "BLOCKED:" + type(e).__name__ + result + """.formatted(escapedPathFunction(), file); + } + + private static String tracebackSourceLineScript(String file) { + return """ + import io + import traceback + + def read_file(host_path, max_lines=100): + # Ask traceback rendering for source lines associated with a compiled + # code object's host filename. The filename is the raw host path, not a + # language-home-prefixed traversal path. + out = [] + for n in range(1, max_lines + 1): + src = "\\n" * (n - 1) + "1/0\\n" + buf = io.StringIO() + try: + exec(compile(src, host_path, "exec")) + except ZeroDivisionError: + traceback.print_exc(file=buf) + line = None + for ln in buf.getvalue().splitlines(): + s = ln.strip() + if s and not s.startswith(("Traceback", "File ", "ZeroDivisionError", "~", "1/0")): + line = s + break + if line is None: + break + out.append(line) + return out + + try: + lines = read_file(%s) + result = "LEAKED:" + "\\n".join(lines) if lines else "BLOCKED:no-source-line" + except BaseException as e: + result = "BLOCKED:" + type(e).__name__ + result + """.formatted(file); + } + + private static String escapedPathFunction() { + return """ + def escaped_path(target): + import os + # __graalpython__.home is PythonContext.langHome, which is what + # isPyFileInLanguageHome compares against. + home = __graalpython__.home + # Count non-empty path elements so the generated path climbs from the + # absolute language home back to the filesystem root. The raw path still + # starts with home, but its normalized form is the supplied host target. + parts = [p for p in home.split(os.sep) if p] + return home + (os.sep + "..") * len(parts) + target + """; + } + + private static String pythonStringLiteral(String value) { + return "'" + value.replace("\\", "\\\\").replace("'", "\\'") + "'"; + } +} 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..895ca8f050 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 @@ -40,6 +40,8 @@ */ package com.oracle.graal.python.test.integration.advanced; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -59,6 +61,7 @@ public class ShutdownTest extends PythonTests { @BeforeClass public static void setUpClass() { Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("mac")); + Assume.assumeTrue(SUPPORTS_PANAMA); } @Test diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/builtin/HashingTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/builtin/HashingTest.java index 17dc9bdc5a..749b357add 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/builtin/HashingTest.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/builtin/HashingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, 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 @@ -197,8 +197,9 @@ public void dictEqualTest3() { @Test public void setAndTest() { - assertPrints("{2}\n", "print({2, 3} ^ {3})\n"); - assertPrints("{'c', 'b'}\n", "print({'a', 'c'} ^ frozenset({'a', 'b'}))\n"); - assertPrints("frozenset({'b'})\n", "print(frozenset({'a', 'c'}) ^ {'a', 'b', 'c'})\n"); + String source = "assert {2, 3} ^ {3} == {2}\n" + + "assert {'a', 'c'} ^ frozenset({'a', 'b'}) == {'b', 'c'}\n" + + "assert frozenset({'a', 'c'}) ^ {'a', 'b', 'c'} == frozenset({'b'})\n"; + assertPrints("", source); } } diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/engine/SharedEngineMultithreadingBenchmarkTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/engine/SharedEngineMultithreadingBenchmarkTest.java index dd950b7189..b56fc879a9 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/engine/SharedEngineMultithreadingBenchmarkTest.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/engine/SharedEngineMultithreadingBenchmarkTest.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,7 +54,7 @@ public class SharedEngineMultithreadingBenchmarkTest extends SharedEngineMultithreadingTestBase { @BeforeClass public static void setUpClass() { - Assume.assumeFalse(isMacOS() || isAArch64()); + Assume.assumeFalse(isMacOS()); } @Test diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java index a78d825b0f..7958946fe6 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/interop/JavaInteropTest.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 @@ -70,7 +70,6 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.runners.Enclosed; import org.junit.runner.RunWith; @@ -485,7 +484,6 @@ public Object execute(Value... arguments) { } } - @Ignore // blocked by GR-46281 @Test public void runAForeignExecutable() throws IOException { Source suitePy = Source.newBuilder("python", @@ -493,7 +491,7 @@ public void runAForeignExecutable() throws IOException { def foo(obj): try: obj() - except TypeError as e: + except: pass else: assert False @@ -504,7 +502,6 @@ def foo(obj): foo.execute(new AForeignExecutable()); } - @Ignore // blocked by GR-46281 @Test public void invokeAForeignMember() throws IOException { Source suitePy = Source.newBuilder("python", @@ -512,7 +509,7 @@ public void invokeAForeignMember() throws IOException { def foo(obj): try: obj.fun() - except TypeError as e: + except: pass else: assert False diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/LzmaTests.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/LzmaTests.java index 0cb48b5c6e..fc28d5e072 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/LzmaTests.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/module/LzmaTests.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 diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/scripts/gr68715_collect_outer_stack_locals.py b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/scripts/gr68715_collect_outer_stack_locals.py new file mode 100644 index 0000000000..19cb9836c6 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/scripts/gr68715_collect_outer_stack_locals.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +# 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. + +import inspect +import sys +import types +from functools import wraps + + +def make_fast_depends_utils_module(): + # Model the frame-walking dependency-injection helper shape found in the wild. + module = types.ModuleType("autogen.fast_depends.utils") + filename = "autogen/fast_depends/utils.py" + module.__file__ = filename + code = r''' +import inspect + + +def collect_outer_stack_locals(): + frame = inspect.currentframe() + current_filename = __file__ + locals_ = {} + while frame: + frame_filename = frame.f_code.co_filename + if frame_filename != current_filename: + locals_.update(frame.f_locals) + frame = frame.f_back + return locals_ +''' + exec(compile(code, filename, "exec"), module.__dict__) + return module + + +fast_depends_utils = make_fast_depends_utils_module() +collect_outer_stack_locals = fast_depends_utils.collect_outer_stack_locals + + +class Depends: + def __init__(self, dependency): + self.dependency = dependency + + +def get_typed_signature(call): + outer_locals = collect_outer_stack_locals() + signature = inspect.signature(call) + resolved = [] + for name, parameter in signature.parameters.items(): + annotation = parameter.annotation + if isinstance(annotation, str): + try: + annotation = eval(annotation, call.__globals__, outer_locals) + except NameError: + annotation = object + resolved.append((name, annotation, parameter.default)) + return resolved + + +def build_call_model(call, depth): + typed_signature = get_typed_signature(call) + if depth <= 0: + return typed_signature + for _, _, default in typed_signature: + if isinstance(default, Depends): + build_call_model(default.dependency, depth - 1) + return typed_signature + + +def inject(call=None, *, depth=3): + def decorate(func): + @wraps(func) + def func_wrapper(*args, **kwargs): + build_call_model(func, depth) + return func(*args, **kwargs) + + return func_wrapper + + if call is None: + return decorate + return decorate(call) + + +def make_dependency_chain(index, depth): + class LocalPayload: + __slots__ = ("value",) + + def __init__(self, value): + self.value = value + + def leaf(item: "LocalPayload" = None): + return item.value if item else index + + dependency = leaf + for layer in range(depth): + previous = dependency + + def dependency(item: "LocalPayload" = None, dep=Depends(previous), layer=layer): + if item is None: + item = LocalPayload(index + layer) + return dep.dependency(item) + + return dependency + + +def generator_adapter(value): + yield value + yield value + 1 + + +def make_tool(index, depth): + dependency = make_dependency_chain(index, depth) + + @inject(depth=depth) + def tool(payload: "LocalPayload" = None, dep=Depends(dependency)): + return sum(value for value in generator_adapter(dep.dependency(payload))) + + class LocalPayload: + __slots__ = ("value",) + + def __init__(self, value): + self.value = value + + return tool + + +def register_for_execution(functions): + registry = {} + + def decorator(func): + build_call_model(func, 2) + registry[func.__name__] = func + functions.append(func) + return func + + return decorator, registry + + +def create_agent(function_count, depth): + functions = [] + decorator, registry = register_for_execution(functions) + for index in range(function_count): + decorator(make_tool(index, depth)) + return registry, functions + + +def invoke_workflow(functions, iterations): + total = 0 + for _ in range(iterations): + for func in functions: + total += func() + return total + + +def main(): + iterations = int(sys.argv[1]) if len(sys.argv) > 1 else 1000 + function_count = int(sys.argv[2]) if len(sys.argv) > 2 else 4 + depth = int(sys.argv[3]) if len(sys.argv) > 3 else 4 + _, functions = create_agent(function_count, depth) + print("done result=%d" % invoke_workflow(functions, iterations)) + + +if __name__ == "__main__": + main() diff --git a/graalpython/com.oracle.graal.python.test/AGENTS.md b/graalpython/com.oracle.graal.python.test/AGENTS.md new file mode 100644 index 0000000000..2969e8ca18 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/AGENTS.md @@ -0,0 +1,19 @@ +# com.oracle.graal.python.test/ — TEST SUITES + +## OVERVIEW +Python-level tests, tag files, and harnesses used by `mx python-gate` and `mx graalpytest`. + +## WHERE TO LOOK +| Task | Location | Notes | +|------|----------|-------| +| Python tests | `src/tests/` | Main Python tests; includes C-API related tests. | +| Tagged/unittest tiers | `src/tests/unittest_tags/` | Tag files select which stdlib tests should pass. | +| Test runner | `src/runner.py` | Harness for executing Python tests. | +| Test data | `testData/` | Golden files and fixtures. | + +## CONVENTIONS +- Tests are typically run via `mx python-gate --tags ...` rather than invoking pytest directly. +- Use `mx [-d] graalpytest TEST-SELECTOR` to rerun a failing test or selection. + +## ANTI-PATTERNS +- Don’t delete failing tests; fix root causes or update tag selections when behavior intentionally changes. 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..1c6f3e8406 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 @@ -41,11 +41,19 @@ package com.oracle.graal.python.cext.test; import static com.oracle.graal.python.cext.test.MultithreadedImportTestBase.multithreadedImportTest; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; 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(SUPPORTS_PANAMA); + } + @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..6d27eedf37 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 @@ -40,10 +40,19 @@ */ package com.oracle.graal.python.cext.test; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; + 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(SUPPORTS_PANAMA); + } + @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/SocketTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/SocketTests.java index 10e2de899c..df3344a028 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/SocketTests.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/SocketTests.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 @@ -74,6 +74,7 @@ import static com.oracle.graal.python.runtime.PosixConstants.SO_TYPE; import static com.oracle.graal.python.runtime.PosixConstants.TCP_NODELAY; import static com.oracle.graal.python.runtime.PosixConstants.TCP_USER_TIMEOUT; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; @@ -123,6 +124,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet6SockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException; import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidUnixSocketPathException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult; import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult; @@ -141,7 +143,7 @@ public class SocketTests { @Parameters(name = "{0}") public static String[] params() { - return new String[]{"native", "java"}; + return SUPPORTS_PANAMA ? new String[]{"java", "native"} : new String[]{"java"}; } @Rule public WithPythonContextRule withPythonContextRule = new WithPythonContextRule((o) -> o.put("python.PosixModuleBackend", backendName)); @@ -170,20 +172,20 @@ public void setUp() { } @Test - public void fillUniversalSockAddrInet4() { + public void fillUniversalSockAddrInet4() throws PosixException { Inet4SockAddr addr = new Inet4SockAddr(12345, INADDR_LOOPBACK.value); checkUsa(addr, createUsa(addr)); } @Test - public void fillUniversalSockAddrInet6() { + public void fillUniversalSockAddrInet6() throws PosixException { assumeTrue(isInet6Supported()); Inet6SockAddr addr = new Inet6SockAddr(12345, IN6ADDR_LOOPBACK, 12, 1); checkUsa(addr, createUsa(addr)); } @Test - public void fillUniversalSockAddrUnix() { + public void fillUniversalSockAddrUnix() throws PosixException { assumeTrue("native".equals(backendName)); byte[] path = new byte[]{65, 0}; UnixSockAddr addr = new UnixSockAddr(path); @@ -638,7 +640,7 @@ public void nonBlockingAccept() throws PosixException { lib.accept(posixSupport, srv.fd); fail("Expected accept() to fail with EWOULDBLOCK"); } catch (PosixException e) { - assertEquals(OSErrorEnum.EWOULDBLOCK.getNumber(), e.getErrorCode()); + assertTrue(e.hasErrno(OSErrorEnum.EWOULDBLOCK)); } cli.connect(srv.usa()); srv.accept(cli.address()); @@ -698,7 +700,7 @@ public void streamSelect() throws PosixException { } @Test - public void getnameinfo() throws GetAddrInfoException { + public void getnameinfo() throws PosixException, GetAddrInfoException { Object[] res = lib.getnameinfo(posixSupport, createUsa(new Inet6SockAddr(443, IN6ADDR_LOOPBACK, 0, 0)), NI_NUMERICSERV.value | NI_NUMERICHOST.value); assertThat(p2s(res[0]), anyOf(equalTo("::1"), equalTo("0:0:0:0:0:0:0:1%0"))); assertEquals("443", p2s(res[1])); @@ -712,7 +714,7 @@ public void getnameinfo() throws GetAddrInfoException { } @Test - public void getnameinfoUdp() throws GetAddrInfoException { + public void getnameinfoUdp() throws PosixException, GetAddrInfoException { assumeTrue(runsOnLinux()); Object[] res = lib.getnameinfo(posixSupport, createUsa(new Inet4SockAddr(512, INADDR_LOOPBACK.value)), NI_NUMERICHOST.value); assertEquals("exec", p2s(res[1])); @@ -774,7 +776,7 @@ public void getaddrinfoBadFlags() { } @Test - public void getaddrinfoServiceOnly() throws GetAddrInfoException { + public void getaddrinfoServiceOnly() throws PosixException, GetAddrInfoException { Object service = s2p("https"); AddrInfoCursor aic = lib.getaddrinfo(posixSupport, null, service, AF_UNSPEC.value, SOCK_STREAM.value, 0, 0); cleanup.add(() -> aicLib.release(aic)); @@ -795,7 +797,7 @@ public void getaddrinfoServiceOnly() throws GetAddrInfoException { } @Test - public void getaddrinfoPassive() throws GetAddrInfoException { + public void getaddrinfoPassive() throws PosixException, GetAddrInfoException { Object service = s2p("https"); AddrInfoCursor aic = lib.getaddrinfo(posixSupport, null, service, AF_INET.value, 0, IPPROTO_TCP.value, AI_PASSIVE.value); cleanup.add(() -> aicLib.release(aic)); @@ -811,7 +813,7 @@ public void getaddrinfoPassive() throws GetAddrInfoException { } @Test - public void getaddrinfoServerOnlyNoCanon() throws GetAddrInfoException { + public void getaddrinfoServerOnlyNoCanon() throws PosixException, GetAddrInfoException { Object node = s2p("localhost"); AddrInfoCursor aic = lib.getaddrinfo(posixSupport, node, null, AF_UNSPEC.value, SOCK_DGRAM.value, 0, 0); cleanup.add(() -> aicLib.release(aic)); @@ -831,7 +833,7 @@ public void getaddrinfoServerOnlyNoCanon() throws GetAddrInfoException { } @Test - public void getaddrinfo() throws GetAddrInfoException { + public void getaddrinfo() throws PosixException, GetAddrInfoException { Object node = s2p("localhost"); Object service = s2p("https"); AddrInfoCursor aic = lib.getaddrinfo(posixSupport, node, service, AF_INET.value, 0, IPPROTO_TCP.value, AI_CANONNAME.value); @@ -1066,7 +1068,7 @@ private String p2s(Object p) { } private static void expectErrno(ThrowingRunnable runnable, OSErrorEnum... expectedErrorCodes) { - PosixException exception = Assert.assertThrows(PosixException.class, runnable); + PosixErrnoException exception = Assert.assertThrows(PosixErrnoException.class, runnable); if (Stream.of(expectedErrorCodes).noneMatch(e -> exception.getErrorCode() == e.getNumber())) { String names = Stream.of(expectedErrorCodes).map(OSErrorEnum::name).collect(Collectors.joining(" or ")); fail("Expected PosixException with error code " + names + " but the actual error code was " + exception.getErrorCode() + " (" + exception + ")"); @@ -1086,7 +1088,7 @@ private int createSocket(int family, int type, int protocol) throws PosixExcepti return sockfd; } - private UniversalSockAddr createUsa(FamilySpecificSockAddr src) { + private UniversalSockAddr createUsa(FamilySpecificSockAddr src) throws PosixException { if (src instanceof Inet4SockAddr inet4SockAddr) { return lib.createUniversalSockAddrInet4(posixSupport, inet4SockAddr); } else if (src instanceof Inet6SockAddr inet6SockAddr) { diff --git a/graalpython/graalpy-jbang/examples/hello.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/CompilerFailureExitTest.java similarity index 58% rename from graalpython/graalpy-jbang/examples/hello.java rename to graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/CompilerFailureExitTest.java index 7e8d1653a2..3185a36e8f 100644 --- a/graalpython/graalpy-jbang/examples/hello.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/CompilerFailureExitTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -38,37 +38,34 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -///usr/bin/env jbang "$0" "$@" ; exit $? -//JAVA 17+ -//DEPS org.graalvm.python:jbang:${env.GRAALPY_VERSION:25.1.0} -// specify python packages and their versions as if used with pip -//PIP termcolor==2.2 +package com.oracle.graal.python.test.advanced; import org.graalvm.polyglot.Context; -import org.graalvm.polyglot.PolyglotException; -import org.graalvm.python.embedding.GraalPyResources; +import org.graalvm.polyglot.Engine; +import org.junit.Assume; +import org.junit.Test; -public class hello { - public static void main(String[] args) { - System.out.println("Running main method from Java."); - try (Context context = GraalPyResources.createContext()) { - switch (args.length) { - case 0: - context.eval("python", "from termcolor import colored; print(print(colored('hello java', 'red', attrs=['reverse', 'blink'])))"); - break; - case 1: - context.eval("python", args[0]); - break; - default: - throw new IllegalArgumentException("The main() helper only takes 0-1 arguments."); - } - } catch (PolyglotException e) { - if (e.isExit()) { - System.exit(e.getExitStatus()); - } else { - throw e; - } +public class CompilerFailureExitTest { + private static final String ENABLE_FLAG_ENV_NAME = "GRAALPYTHON_JUNIT_COMPILER_FAILURE_EXIT_TEST"; + + @Test + public void compilerBailoutExitsVM() { + Assume.assumeTrue(ENABLE_FLAG_ENV_NAME + " is not set", "true".equals(System.getenv(ENABLE_FLAG_ENV_NAME))); + try (Engine engine = Engine.newBuilder("python").allowExperimentalOptions(true).// + option("engine.BackgroundCompilation", "false").// + option("engine.FirstTierCompilationThreshold", "1").// + option("engine.LastTierCompilationThreshold", "10").build(); + Context context = Context.newBuilder("python").engine(engine).allowExperimentalOptions(true).allowAllAccess(true).// + option("python.EnableDebuggingBuiltins", "true").build()) { + context.eval("python", """ + import __graalpython__ + + def trigger_compiler_bailout(): + for _ in range(100): + __graalpython__.compiler_bailout_for_tests() + + trigger_compiler_bailout() + """); } } } - diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/LeakTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/LeakTest.java index 2bef8eaf9b..f6e9bbfb81 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/LeakTest.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/advanced/LeakTest.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 @@ -66,7 +66,9 @@ import org.netbeans.lib.profiler.heap.HeapFactory; import org.netbeans.lib.profiler.heap.Instance; import org.netbeans.lib.profiler.heap.JavaClass; +import org.netbeans.lib.profiler.heap.ObjectArrayInstance; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandleContext; import com.oracle.graal.python.test.integration.Utils; import com.sun.management.HotSpotDiagnosticMXBean; @@ -100,6 +102,7 @@ public static void main(String[] args) { private boolean keepDump = false; private int repeatAndCheckSize = -1; private boolean nullStdout = false; + private boolean forbidCApiResidue = false; private String languageId; private String code; private List forbiddenClasses = new ArrayList<>(); @@ -161,19 +164,99 @@ private boolean checkForLeaks(Path dumpFile) { } } } + if (forbidCApiResidue && checkCApiResidue(heap)) { + fail = true; + } } catch (IOException e) { throw new RuntimeException(e); } return fail; } + private boolean checkCApiResidue(Heap heap) { + JavaClass cls = heap.getJavaClassByName(HandleContext.class.getName()); + if (cls == null) { + System.err.println("Could not find " + HandleContext.class.getName() + " in heap dump"); + return true; + } + boolean fail = false; + for (Object i : cls.getInstances()) { + Instance inst = (Instance) i; + if (!isReachable(inst)) { + continue; + } + List residues = new ArrayList<>(); + addResidue(residues, inst, "referencesToBeFreed", this::collectionSize); + addResidue(residues, inst, "nativeLookup", this::collectionSize); + addResidue(residues, inst, "nativeWeakRef", this::collectionSize); + addResidue(residues, inst, "nativeTypeLookup", this::objectArraySize); + addResidue(residues, inst, "nativeStubLookup", this::objectArraySize); + addResidue(residues, inst, "nativeStorageReferences", this::collectionSize); + addResidue(residues, inst, "pyCapsuleReferences", this::collectionSize); + if (!residues.isEmpty()) { + fail = true; + System.err.println("C API residue in reachable HandleContext " + inst.getInstanceId() + ": " + + String.join(", ", residues)); + } + } + return fail; + } + + private void addResidue(List residues, Instance inst, String name, FieldSize fieldSize) { + Object fieldValue = inst.getValueOfField(name); + if (fieldValue == null) { + residues.add(name + "=missing"); + return; + } + int size = fieldSize.apply(fieldValue); + if (size > 0) { + residues.add(name + "=" + size); + } + } + + @FunctionalInterface + private interface FieldSize { + int apply(Object object); + } + + private int collectionSize(Object object) { + if (object instanceof Instance instance) { + Object size = instance.getValueOfField("size"); + if (size instanceof Number n) { + return n.intValue(); + } + Object baseCount = instance.getValueOfField("baseCount"); + if (baseCount instanceof Number n) { + return n.intValue(); + } + Object map = instance.getValueOfField("map"); + if (map instanceof Instance mapInstance) { + return collectionSize(mapInstance); + } + } + return 0; + } + + private int objectArraySize(Object object) { + if (object instanceof ObjectArrayInstance array) { + int size = 0; + for (Object value : array.getValues()) { + if (value != null) { + size++; + } + } + return size; + } + return 0; + } + private int getCntAndErrors(JavaClass cls, List errors) { int cnt = cls.getInstancesCount(); if (cnt > 0) { boolean realLeak = false; for (Object i : cls.getInstances()) { Instance inst = (Instance) i; - if (inst.isGCRoot() || inst.getNearestGCRootPointer() != null) { + if (isReachable(inst)) { realLeak = true; break; } @@ -188,6 +271,10 @@ private int getCntAndErrors(JavaClass cls, List errors) { return cnt; } + private boolean isReachable(Instance inst) { + return inst.isGCRoot() || inst.getNearestGCRootPointer() != null; + } + @SuppressWarnings("sync-override") @Override public final Throwable fillInStackTrace() { @@ -271,6 +358,8 @@ protected List preprocessArguments(List arguments, Map 0]}", + f"strftime_tuple={time.strftime('%Z', tt)}", + f"strftime_now={time.strftime('%Z')}", + )) + """)).asString(); + } + + Map values = parseKeyValueLines(result); + String details = values.toString(); + assertEquals(details, values.get("tm_zone"), values.get("tzname")); + assertEquals(details, values.get("tm_zone"), values.get("strftime_tuple")); + assertEquals(details, values.get("tm_zone"), values.get("strftime_now")); + } finally { + TimeZone.setDefault(previousDefault); + } + } + + private static Map parseKeyValueLines(String output) { + Map values = new LinkedHashMap<>(); + for (String line : output.split("\\R")) { + int separator = line.indexOf('='); + values.put(line.substring(0, separator), line.substring(separator + 1)); + } + return values; + } +} 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..5e008508c1 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 @@ -40,27 +40,51 @@ */ package com.oracle.graal.python.test.builtin.objects; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; + 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.annotations.NativeSimpleType; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TpSlots.Builder; import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotGroup; 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.util.Function; public class TpSlotsTests { + private NativeContext nativeContext; + + @BeforeClass + public static void setUpClass() { + Assume.assumeTrue(SUPPORTS_PANAMA); + } + + @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 +119,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 +135,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 +161,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..9970dc920c 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 @@ -40,16 +40,26 @@ */ package com.oracle.graal.python.test.builtin.objects.cext; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; 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 +68,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(SUPPORTS_PANAMA); + } + + @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/builtin/objects/dict/PDictTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/dict/PDictTest.java index 30450f9d61..be6ce8d515 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/dict/PDictTest.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/dict/PDictTest.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 @@ -98,6 +98,7 @@ public void economicMapStorageSet() { dict.setItem(ts("key1"), 42); assertEquals(2, length(dict)); + assertTrue(dict.getDictStorage() instanceof EconomicMapStorage); assertEquals(42, dict.getItem(ts("key1"))); } @@ -113,9 +114,20 @@ public void economicMapStorageDel() { delItem(dict, ts("key2")); assertEquals(2, length(dict)); + assertTrue(dict.getDictStorage() instanceof EconomicMapStorage); assertEquals(42, dict.getItem(ts("key1"))); assertNull(dict.getItem(ts("key2"))); } + + @Test + public void economicMapStorageEightEntries() { + PDict dict = PFactory.createDict(PythonLanguage.get(null)); + for (int i = 0; i < 8; i++) { + dict.setItem(i, i); + } + assertTrue(dict.getDictStorage() instanceof EconomicMapStorage); + assertEquals(8, length(dict)); + } } diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/debug/PythonDebugTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/debug/PythonDebugTest.java index f6cca17770..1d8b5fa3f6 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/debug/PythonDebugTest.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/debug/PythonDebugTest.java @@ -82,6 +82,7 @@ public void before() { Builder newBuilder = Context.newBuilder(); newBuilder.allowExperimentalOptions(true); newBuilder.allowAllAccess(true); + newBuilder.option("engine.WarnInterpreterOnly", "false"); PythonTests.closeContext(); tester = new DebuggerTester(newBuilder); } @@ -549,6 +550,15 @@ public void testInspectJavaArray() throws Throwable { @Test public void testSourceFileURI() throws Throwable { + testSourceFileURIImpl(false); + } + + @Test + public void testSourceFileURIBytecode() throws Throwable { + testSourceFileURIImpl(true); + } + + private void testSourceFileURIImpl(boolean runFromBytecode) throws Throwable { if (System.getProperty("os.name").toLowerCase().contains("mac")) { // on the mac machines we run with symlinked directories and such, and it's annoying to // cater for that @@ -564,6 +574,11 @@ public void testSourceFileURI() throws Throwable { "sys.path.insert(0, '" + tempDir.toString() + "')\n" + "import imported\n" + "imported.sum(2, 3)\n").getBytes()); + + if (runFromBytecode) { + compileToBytecode(importedFile, importingFile); + } + Source source = Source.newBuilder("python", importingFile.toFile()).build(); try (DebuggerSession session = tester.startSession()) { Breakpoint breakpoint = Breakpoint.newBuilder(importingFile.toUri()).lineIs(4).build(); @@ -601,6 +616,47 @@ public void testSourceFileURI() throws Throwable { } } + private void compileToBytecode(Path... files) { + StringBuilder sourceCode = new StringBuilder("import py_compile\n"); + for (Path file : files) { + sourceCode.append("py_compile.compile(r\"").append(file).append("\")\n"); + } + Source compileSource = Source.newBuilder("python", sourceCode.toString(), "compile_source_uri.py").buildLiteral(); + tester.startEval(compileSource); + tester.expectDone(); + } + + @Test + public void testInlineEvaluationBreakpointBuiltin() throws Throwable { + final Source source = Source.newBuilder("python", """ + a = 1 + breakpoint() + b = 2 + breakpoint # not invoking, therefore no breakpoint inserted + """, "test_inline.py").buildLiteral(); + + try (DebuggerSession session = tester.startSession()) { + session.install(Breakpoint.newBuilder(DebuggerTester.getSourceImpl(source)).lineIs(1).build()); + session.install(Breakpoint.newBuilder(DebuggerTester.getSourceImpl(source)).lineIs(3).build()); + tester.startEval(source); + expectSuspended((SuspendedEvent event) -> { + DebugStackFrame frame = event.getTopStackFrame(); + assertEquals(1, frame.getSourceSection().getStartLine()); + event.prepareContinue(); + }); + expectSuspended((SuspendedEvent event) -> { + DebugStackFrame frame = event.getTopStackFrame(); + assertEquals(2, frame.getSourceSection().getStartLine()); + event.prepareContinue(); + }); + expectSuspended((SuspendedEvent event) -> { + DebugStackFrame frame = event.getTopStackFrame(); + assertEquals(3, frame.getSourceSection().getStartLine()); + event.prepareContinue(); + }); + } + } + private void expectSuspended(SuspendedCallback callback) { tester.expectSuspended(callback); } diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/ArgsKwArgsTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/ArgsKwArgsTest.java index b81ccd74d7..fefe2f9209 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/ArgsKwArgsTest.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/ArgsKwArgsTest.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,6 +41,7 @@ package com.oracle.graal.python.test.interop; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -77,6 +78,35 @@ private Value run(String evalString) { return context.eval("python", evalString); } + @Test + public void testLazyModuleMemberInvoke() { + Value module = run(""" + import types + + class LazyModule(types.ModuleType): + def __getattr__(self, name): + if name == "set_seed": + def set_seed(seed): + self.seed = seed + return seed + 1 + self.__dict__[name] = set_seed + return set_seed + raise AttributeError(name) + + direct = LazyModule("direct") + probe = LazyModule("probe") + """); + + Value direct = module.getMember("direct"); + assertEquals(44, direct.invokeMember("set_seed", 43).asInt()); + assertEquals(43, direct.getMember("seed").asInt()); + + Value probe = module.getMember("probe"); + assertTrue(probe.hasMember("set_seed")); + assertFalse(probe.hasMember("missing")); + assertEquals(12, probe.invokeMember("set_seed", 11).asInt()); + } + @Test public void testPositionalArgs01() { // @formatter:off diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/HostInteropTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/HostInteropTest.java index ab99e374da..4d54def051 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/HostInteropTest.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/HostInteropTest.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 @@ -46,6 +46,7 @@ import static org.junit.Assert.assertTrue; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.time.LocalDate; import java.time.LocalTime; @@ -53,6 +54,7 @@ import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.io.ByteSequence; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -713,4 +715,79 @@ public void testByteBuffer() { t.writeBufferDouble(ByteOrder.LITTLE_ENDIAN, 0, 12345.6789123); assertEquals(12345.6789123, t.readBufferDouble(ByteOrder.LITTLE_ENDIAN, 0), 0.0); } + + @Test + public void testHostByteBufferAsPythonBuffer() { + byte[] writable = new byte[]{1, 2, 3, 4}; + context.getBindings("python").putMember("writable_bb", ByteBuffer.wrap(writable)); + context.getBindings("python").putMember("readonly_bb", ByteBuffer.wrap(new byte[]{-1, 5, 6, 7, 8}).asReadOnlyBuffer()); + + context.eval("python", """ + import binascii + import io + + mv = memoryview(writable_bb) + assert not mv.readonly + assert mv.tobytes() == b"\\x01\\x02\\x03\\x04" + assert bytes(writable_bb) == b"\\x01\\x02\\x03\\x04" + assert bytearray(writable_bb) == bytearray(b"\\x01\\x02\\x03\\x04") + assert binascii.hexlify(writable_bb) == b"01020304" + bio = io.BytesIO() + assert bio.write(writable_bb) == 4 + assert bio.getvalue() == b"\\x01\\x02\\x03\\x04" + mv[1] = 9 + assert io.BytesIO(b"abcd").readinto(writable_bb) == 4 + assert bytes(writable_bb) == b"abcd" + + ro = memoryview(readonly_bb) + assert ro.readonly + assert ro.tobytes() == b"\\xff\\x05\\x06\\x07\\x08" + assert bytes(readonly_bb) == b"\\xff\\x05\\x06\\x07\\x08" + assert bytearray(readonly_bb) == bytearray(b"\\xff\\x05\\x06\\x07\\x08") + assert io.BytesIO().write(readonly_bb) == 5 + try: + ro[0] = 1 + raise AssertionError("expected memoryview write to fail") + except TypeError: + pass + try: + io.BytesIO(b"wxyz").readinto(readonly_bb) + raise AssertionError("expected readinto to fail") + except TypeError: + pass + """); + + assertArrayEquals(new byte[]{'a', 'b', 'c', 'd'}, writable); + } + + @Test + public void testHostByteSequenceAsPythonBuffer() { + byte[] bytes = new byte[]{10, 20, 30, 40}; + context.getBindings("python").putMember("seq", ByteSequence.create(bytes)); + + context.eval("python", """ + import binascii + import io + + mv = memoryview(seq) + assert mv.readonly + assert mv.tobytes() == b"\\x0a\\x14\\x1e\\x28" + assert bytes(seq) == b"\\x0a\\x14\\x1e\\x28" + assert bytearray(seq) == bytearray(b"\\x0a\\x14\\x1e\\x28") + assert binascii.hexlify(seq) == b"0a141e28" + bio = io.BytesIO() + assert bio.write(seq) == 4 + assert bio.getvalue() == b"\\x0a\\x14\\x1e\\x28" + try: + mv[0] = 1 + raise AssertionError("expected memoryview write to fail") + except TypeError: + pass + try: + io.BytesIO(b"abcd").readinto(seq) + raise AssertionError("expected readinto to fail") + except TypeError: + pass + """); + } } diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/SourceOptionsTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/SourceOptionsTests.java new file mode 100644 index 0000000000..f6acf93a86 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/interop/SourceOptionsTests.java @@ -0,0 +1,95 @@ +/* + * 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.test.interop; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Source; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.oracle.graal.python.test.PythonTests; + +public class SourceOptionsTests extends PythonTests { + private Context context; + + @Before + public void setUpTest() { + Context.Builder builder = Context.newBuilder(); + builder.allowExperimentalOptions(true); + builder.allowAllAccess(true); + context = builder.build(); + } + + @After + public void tearDown() { + context.close(); + } + + @Test + public void testDefaultUsesMainModuleGlobals() { + context.eval("python", "x = 41"); + assertEquals(42, context.eval("python", "x + 1").asInt()); + } + + @Test + public void testNewGlobalsIsolatedFromMainModule() throws IOException { + context.eval("python", "x = 41"); + + Source source = Source.newBuilder("python", "x = 100\nx + 1", "new-globals.py").option("python.NewGlobals", "true").build(); + + assertEquals(101, context.eval(source).asInt()); + assertEquals(42, context.eval("python", "x + 1").asInt()); + } + + @Test + public void testSeparateNewGlobalsExecutionsDoNotShareState() throws IOException { + Source writeSource = Source.newBuilder("python", "x = 100", "new-globals-write.py").option("python.NewGlobals", "true").build(); + Source readSource = Source.newBuilder("python", "x = globals().get('x', 0)\nx + 1", "new-globals-read.py").option("python.NewGlobals", "true").build(); + + context.eval(writeSource); + assertEquals(1, context.eval(readSource).asInt()); + } +} 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..a9fec087cb 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 @@ -40,6 +40,8 @@ */ package com.oracle.graal.python.test.nodes; +import static com.oracle.graal.python.test.integration.Utils.SUPPORTS_PANAMA; + 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.common.SequenceStorageNodes.MemMoveNode; @@ -68,6 +70,7 @@ public class MemMoveNodeTests { @BeforeClass public static void setUpClass() { Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("mac")); + Assume.assumeTrue(SUPPORTS_PANAMA); } @Before diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/objects/ObjectHashMapTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/objects/ObjectHashMapTests.java index 18dcf485a3..d209b42c56 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/objects/ObjectHashMapTests.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/objects/ObjectHashMapTests.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 @@ -74,9 +74,6 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.interop.TruffleObject; 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.InlinedCountingConditionProfile; public class ObjectHashMapTests { public static final class DictKey implements TruffleObject { @@ -173,7 +170,7 @@ public void testLongHashMapStressTest() { // Basic tests of other methods Object[] oldKeys = keysToArray(map); - ObjectHashMap copy = map.copy(); + ObjectHashMap copy = copyMap(map); assertEquals(map.size(), copy.size()); for (Object key : oldKeys) { assertEquals(key.toString(), // @@ -220,8 +217,7 @@ private static void popValues(ObjectHashMap map, LinkedHashMap exp var keys = expected.keySet().stream().toList().reversed().stream().limit(count).toArray(Long[]::new); for (int i = 0; i < keys.length; i++) { Long key = keys[i]; - Object[] popped = PopNode.doPopWithRestart(null, map, InlinedConditionProfile.getUncached(), InlinedCountingConditionProfile.getUncached(), InlinedCountingConditionProfile.getUncached(), - InlinedBranchProfile.getUncached()); + Object[] popped = PopNode.doPopWithRestartForTests(map); Assert.assertEquals(Integer.toString(i), key, popped[0]); Assert.assertEquals(Integer.toString(i), expected.get(key), popped[1]); expected.remove(key); @@ -315,7 +311,7 @@ static void assertEqual(String message, LinkedHashMap expected, O Collections.reverse(keysValuesReversed); assertArrayEquals(message, keysValuesReversed.toArray(), reverseKeysToArray(actual)); - EconomicMapStorage storage = new EconomicMapStorage(actual, false); + EconomicMapStorage storage = toEconomicMapStorage(actual); int[] size = new int[]{0}; HashingStorageForEach.executeUncached(storage, new HashingStorageForEachCallback<>() { @Override @@ -330,12 +326,12 @@ public Object execute(Frame frame, Node inliningTarget, HashingStorage s, Hashin } private static Object[] keysToArray(ObjectHashMap m) { - EconomicMapStorage s = new EconomicMapStorage(m, false); + EconomicMapStorage s = toEconomicMapStorage(m); return iteratorToArray(s, HashingStorageGetIterator.executeUncached(s)); } private static Object[] reverseKeysToArray(ObjectHashMap m) { - EconomicMapStorage s = new EconomicMapStorage(m, false); + EconomicMapStorage s = toEconomicMapStorage(m); return iteratorToArray(s, HashingStorageGetReverseIterator.executeUncached(s)); } @@ -349,6 +345,24 @@ private static Object[] iteratorToArray(HashingStorage s, HashingStorageIterator private static int valueCounter = 0; + private static ObjectHashMap copyMap(ObjectHashMap original) { + EconomicMapStorage copy = EconomicMapStorage.create(original.size()); + MapCursor cursor = original.getEntries(); + while (cursor.advance()) { + put(copy, cursor.getKey().getValue(), cursor.getKey().getPythonHash(), cursor.getValue()); + } + return copy; + } + + private static EconomicMapStorage toEconomicMapStorage(ObjectHashMap map) { + EconomicMapStorage storage = EconomicMapStorage.create(map.size()); + MapCursor cursor = map.getEntries(); + while (cursor.advance()) { + put(storage, cursor.getKey().getValue(), cursor.getKey().getPythonHash(), cursor.getValue()); + } + return storage; + } + public static Object newValue() { return "Val: " + (valueCounter++); } @@ -358,25 +372,14 @@ private static long getKeyHash(Object key) { } private static Object get(ObjectHashMap map, Object key, long hash) { - InlinedCountingConditionProfile uncachedCounting = InlinedCountingConditionProfile.getUncached(); - return ObjectHashMap.GetNode.doGetWithRestart(null, null, map, key, hash, - InlinedBranchProfile.getUncached(), uncachedCounting, uncachedCounting, uncachedCounting, - uncachedCounting, uncachedCounting, - new EqNodeStub()); + return ObjectHashMap.GetNode.doGetWithRestartForTests(map, key, hash, new EqNodeStub()); } private static void remove(ObjectHashMap map, Object key, long hash) { - InlinedCountingConditionProfile uncachedCounting = InlinedCountingConditionProfile.getUncached(); - ObjectHashMap.RemoveNode.doRemoveWithRestart(null, null, map, key, hash, - InlinedBranchProfile.getUncached(), uncachedCounting, uncachedCounting, uncachedCounting, - uncachedCounting, InlinedBranchProfile.getUncached(), new EqNodeStub()); + ObjectHashMap.RemoveNode.doRemoveWithRestartForTests(map, key, hash, new EqNodeStub()); } private static void put(ObjectHashMap map, Object key, long hash, Object value) { - InlinedCountingConditionProfile uncachedCounting = InlinedCountingConditionProfile.getUncached(); - PutNode.doPutWithRestart(null, null, map, key, hash, value, - InlinedBranchProfile.getUncached(), uncachedCounting, uncachedCounting, uncachedCounting, - uncachedCounting, InlinedBranchProfile.getUncached(), InlinedBranchProfile.getUncached(), - new EqNodeStub()); + PutNode.doPutWithRestartForTests(map, key, hash, value, new EqNodeStub()); } } diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/ParseWithArgumentsInstrumentTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/ParseWithArgumentsInstrumentTests.java new file mode 100644 index 0000000000..9646117a32 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/ParseWithArgumentsInstrumentTests.java @@ -0,0 +1,135 @@ +/* + * 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.test.runtime; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.util.function.Function; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Engine; +import org.graalvm.polyglot.Instrument; +import org.junit.Test; + +import com.oracle.truffle.api.ContextLocal; +import com.oracle.truffle.api.TruffleContext; +import com.oracle.truffle.api.instrumentation.ContextsListener; +import com.oracle.truffle.api.instrumentation.TruffleInstrument; +import com.oracle.truffle.api.nodes.LanguageInfo; + +public class ParseWithArgumentsInstrumentTests { + private static final String TEST_INSTRUMENT_ID = "parse-with-arguments-instrument-tests"; + + @SuppressWarnings("unchecked") + @Test + public void instrumentParseWithArgumentsDuringLanguageContextInitializationExecutes() { + try (Engine engine = Engine.create()) { + Instrument instrument = engine.getInstruments().get(TEST_INSTRUMENT_ID); + Function registrar = instrument.lookup(Function.class); + registrar.apply(org.graalvm.polyglot.Source.create("python", "x + 1")); + try (Context context = Context.newBuilder("python").engine(engine).allowExperimentalOptions(true).allowAllAccess(true).build()) { + assertEquals(42, context.eval("python", "42").asInt()); + } + } + } + + @TruffleInstrument.Registration(id = TEST_INSTRUMENT_ID, services = Function.class) + public static final class ParseWithArgumentsInitializationInstrument extends TruffleInstrument { + private final ContextLocal parsed = locals.createContextLocal((ctx) -> new boolean[1]); + + @Override + protected void onCreate(Env env) { + Function registerSource = (polyglotSource) -> { + com.oracle.truffle.api.source.Source source = com.oracle.truffle.api.source.Source.newBuilder( + polyglotSource.getLanguage(), polyglotSource.getCharacters(), polyglotSource.getName()).build(); + env.getInstrumenter().attachContextsListener(new ContextsListener() { + @Override + public void onContextCreated(TruffleContext context) { + } + + @Override + public void onLanguageContextCreate(TruffleContext context, LanguageInfo language) { + } + + @Override + public void onLanguageContextCreated(TruffleContext context, LanguageInfo language) { + } + + @Override + public void onLanguageContextInitialize(TruffleContext context, LanguageInfo language) { + } + + @Override + public void onLanguageContextInitialized(TruffleContext context, LanguageInfo language) { + if (!"python".equals(language.getId())) { + return; + } + boolean[] seen = parsed.get(context); + if (seen[0]) { + return; + } + seen[0] = true; + try { + env.parse(source, "x").call(41); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public void onLanguageContextFinalized(TruffleContext context, LanguageInfo language) { + } + + @Override + public void onLanguageContextDisposed(TruffleContext context, LanguageInfo language) { + } + + @Override + public void onContextClosed(TruffleContext context) { + } + }, true); + return null; + }; + env.registerService(registerSource); + } + } +} diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/PythonContextEntropyTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/PythonContextEntropyTests.java new file mode 100644 index 0000000000..7dcee55008 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/PythonContextEntropyTests.java @@ -0,0 +1,126 @@ +/* + * 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.test.runtime; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Random; + +import org.graalvm.polyglot.PolyglotException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.Assume; + +import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.test.PythonTests; + +public class PythonContextEntropyTests { + + @Before + public void checkLinuxOnly() { + Assume.assumeTrue(System.getProperty("os.name").toLowerCase().contains("linux")); + } + + @After + public void tearDown() { + PythonTests.closeContext(); + } + + @Test + public void fixedInitializationEntropySourceSeedsHashSecretDeterministically() { + long seed = 0x1234ABCDL; + PythonTests.enterContext(Map.of("python.InitializationEntropySource", "fixed:0x1234ABCD"), new String[0]); + PythonContext context = PythonContext.get(null); + + byte[] expected = new byte[24]; + new Random(seed).nextBytes(expected); + + assertArrayEquals(expected, context.getHashSecret()); + } + + @Test + public void deviceInitializationEntropySourceSeedsHashSecretFromConfiguredPath() throws IOException { + byte[] expected = new byte[24]; + for (int i = 0; i < expected.length; i++) { + expected[i] = (byte) (i + 1); + } + byte[] source = new byte[expected.length + 8]; + System.arraycopy(expected, 0, source, 0, expected.length); + for (int i = expected.length; i < source.length; i++) { + source[i] = (byte) 0xFF; + } + Path tempFile = Files.createTempFile("graalpy-init-entropy-", ".bin"); + Files.write(tempFile, source); + + try { + PythonTests.enterContext(Map.of("python.InitializationEntropySource", "device:" + tempFile), new String[0]); + PythonContext context = PythonContext.get(null); + assertArrayEquals(expected, context.getHashSecret()); + } finally { + Files.deleteIfExists(tempFile); + } + } + + @Test + public void deviceInitializationEntropySourceThrowsProviderExceptionWhenExhausted() throws IOException { + Path tempFile = Files.createTempFile("graalpy-init-entropy-short-", ".bin"); + Files.write(tempFile, new byte[]{1, 2, 3, 4}); + + try { + try { + PythonTests.enterContext(Map.of("python.InitializationEntropySource", "device:" + tempFile), new String[0]); + fail("expected PolyglotException"); + } catch (PolyglotException e) { + assertTrue(e.getMessage().contains("ProviderException")); + assertTrue(e.getMessage().contains("initialization entropy device exhausted")); + } + } finally { + Files.deleteIfExists(tempFile); + } + } +} diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/PythonContextPathTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/PythonContextPathTests.java new file mode 100644 index 0000000000..39ac84456f --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/runtime/PythonContextPathTests.java @@ -0,0 +1,76 @@ +/* + * 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.test.runtime; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; + +import org.junit.After; +import org.junit.Test; + +import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.test.PythonTests; +import com.oracle.truffle.api.TruffleFile; + +public class PythonContextPathTests { + + @After + public void tearDown() { + PythonTests.closeContext(); + } + + @Test + public void isPyFileInLanguageHomeNormalizesPathBeforeContainmentCheck() { + PythonTests.enterContext(); + PythonContext context = PythonContext.get(null); + String languageHomePath = context.getLanguageHome().toJavaStringUncached(); + TruffleFile languageHome = context.getEnv().getInternalTruffleFile(languageHomePath).getAbsoluteFile().normalize(); + assumeFalse("resource-backed language home is rooted at /", "/".equals(languageHome.getPath())); + TruffleFile insideLanguageHome = context.getEnv().getInternalTruffleFile(languageHomePath + "/lib-python"); + TruffleFile escapedLanguageHome = context.getEnv().getInternalTruffleFile(languageHomePath + "/../outside.py"); + + assertTrue(context.isPyFileInLanguageHome(insideLanguageHome)); + assertFalse("escaped path " + escapedLanguageHome.getAbsoluteFile().normalize() + + " should not be contained in language home " + languageHome, + context.isPyFileInLanguageHome(escapedLanguageHome)); + } +} diff --git a/graalpython/com.oracle.graal.python.test/src/runner.py b/graalpython/com.oracle.graal.python.test/src/runner.py index b7e3bbc753..8725692a1c 100644 --- a/graalpython/com.oracle.graal.python.test/src/runner.py +++ b/graalpython/com.oracle.graal.python.test/src/runner.py @@ -83,6 +83,8 @@ RUNNER_ENV = {} DISABLE_JIT_ENV = {'GRAAL_PYTHON_VM_ARGS': '--experimental-options --engine.Compilation=false'} +# The worker transport sends pickled data, so keep it on loopback only. +WORKER_SERVER_HOST = '127.0.0.1' GITHUB_CI = os.environ.get("GITHUB_CI", None) if GITHUB_CI: @@ -546,7 +548,7 @@ def write_tags(test_file: 'TestFile', tags: typing.Iterable['Tag']): tag_file.unlink(missing_ok=True) return with open(tag_file, 'w') as f: - for tag in sorted(tags, key=lambda t: t.test_id.test_name): + for tag in sorted(tags, key=lambda t: (t.test_id.test_name, t.is_exclusion)): f.write(f'{tag}\n') @@ -791,7 +793,7 @@ def run_in_subprocess_and_watch(self): self.thread = threading.current_thread() with ( tempfile.TemporaryDirectory(prefix='graalpytest-') as tmp_dir, - socket.create_server(('0.0.0.0', 0)) as server, + socket.create_server((WORKER_SERVER_HOST, 0)) as server, ): tmp_dir = Path(tmp_dir) @@ -1207,6 +1209,7 @@ def collect(all_specifiers: list[TestSpecifier], *, use_tags=False, ignore=None, to_run.append(collected) return to_run + @dataclass(frozen=True) class Tag: test_id: TestId @@ -1231,7 +1234,6 @@ def without_keys(self, keys: set[str]) -> 'Tag | None': return self return Tag(self.test_id, keys, is_exclusion=self.is_exclusion) - def __str__(self): s = '' if self.is_exclusion: @@ -1267,7 +1269,6 @@ def read_tags(test_file: TestFile, allow_exclusions=False) -> list[Tag]: is_exclusion = True test = test.removeprefix('!') - if not keys and not is_exclusion: log(f'WARNING: invalid tag {test}: missing platform keys') @@ -1311,7 +1312,7 @@ def _recv(self, size): while len(data) < size: read = self.socket.recv(size - len(data)) if not read: - return data + raise ConnectionClosed data += read return data @@ -1328,7 +1329,7 @@ def poll(self, timeout=None): def main_worker(args): - with socket.create_connection(('localhost', args.port)) as sock: + with socket.create_connection((WORKER_SERVER_HOST, args.port)) as sock: conn = Connection(sock) tests = conn.recv() 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 26392bfb0d..0d5bb0ea69 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/__init__.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/__init__.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 @@ -46,13 +46,17 @@ compiled_registry = set() +_venv_site_packages_cache = {} def find_rootdir(): cur_dir = Path(__file__).parent while cur_dir.name != 'graalpython': cur_dir = cur_dir.parent - rootdir = cur_dir.parent / "mxbuild" / "cpyexts" + if (cur_dir / "mx.graalpython").exists(): + rootdir = cur_dir / "mxbuild" / "cpyexts" + else: + rootdir = cur_dir.parent / "mxbuild" / "cpyexts" rootdir.mkdir(parents=True, exist_ok=True) return rootdir @@ -60,34 +64,96 @@ 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_python(venv_dir: Path) -> Path: + if sys.platform.startswith('win32'): + return venv_dir / 'Scripts' / 'python.exe' + return venv_dir / 'bin' / 'python3' - if not os.path.isdir(setuptools_path / 'setuptools'): + +def _venv_site_packages(venv_dir: Path, py_executable: Path = None) -> Path: + # the python creating the venv may be different, so we need to figure out + # the correct path using that python. this is used in polybenchmarks, where + # the executable is not graalpy and thus a different CI-env Python is used. + if py_executable and py_executable.exists(): + cache_key = venv_dir.absolute() + cached_path = _venv_site_packages_cache.get(cache_key) + if cached_path and cached_path.exists(): + return cached_path import subprocess - print('installing setuptools in %s' % setuptools_path) - system_python = install_venv(setuptools_path) - if sys.platform.startswith('win32'): - py_executable = setuptools_path / 'Scripts' / 'python.exe' + try: + path = subprocess.check_output([ + py_executable, + "-c", + "import sysconfig; print(sysconfig.get_paths()['purelib'])", + ], text=True).strip() + except (OSError, subprocess.CalledProcessError): + pass 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) + if path: + site_packages = Path(path) + _venv_site_packages_cache[cache_key] = site_packages + return site_packages + existing_site_packages = list(venv_dir.glob("lib*/python*/site-packages")) + if len(existing_site_packages) == 1: + return existing_site_packages[0] + non_empty_site_packages = [path for path in existing_site_packages if any(path.iterdir())] + if len(non_empty_site_packages) == 1: + return non_empty_site_packages[0] + if sys.platform.startswith('win32'): + return venv_dir / 'Lib' / 'site-packages' + version_dir = f'python{sys.version_info.major}.{sys.version_info.minor}' + lib64_path = venv_dir / 'lib64' / version_dir / 'site-packages' + if lib64_path.exists(): + return lib64_path + return venv_dir / 'lib' / version_dir / '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) + - pyvenv_site = str(setuptools_path) - if pyvenv_site not in site.getsitepackages(): +def ensure_packages(**package_specs): + import site + package_names = "-".join(package_specs.keys()) + venv_dir = find_rootdir() / f'{sys.implementation.name}-{package_names}-venv' + py_executable = _venv_python(venv_dir) + site_packages_dir = _venv_site_packages(venv_dir, py_executable) + if any(not _package_present(site_packages_dir, p, v) for p, v in package_specs.items()): + import subprocess + 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) + site_packages_dir = _venv_site_packages(venv_dir, py_executable) + extra_args = [] + if system_python or sys.implementation.name != "graalpy": + pass + elif __graalpython__.is_bytecode_dsl_interpreter: + extra_args = ['--vm.Dpython.EnableBytecodeDSLInterpreter=true'] + else: + 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(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 @@ -171,7 +237,11 @@ def ccompile(self, name, check_duplicate_name=True): os.chdir(build_dir) try: shutil.copy(source_file, '.') - module = Extension(filename, sources=[source_file.name]) + module = Extension( + filename, + sources=[source_file.name], + define_macros=[("GRAALPY_ENABLE_TESTING_CAPI", "1")], + ) args = [ '--verbose' if sys.flags.verbose else '--quiet', 'build', '--build-temp=t', '--build-base=b', '--build-purelib=l', '--build-platlib=l', diff --git a/graalpython/com.oracle.graal.python.test/src/tests/conftest.toml b/graalpython/com.oracle.graal.python.test/src/tests/conftest.toml index 31d5e58c79..db128a5123 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/conftest.toml +++ b/graalpython/com.oracle.graal.python.test/src/tests/conftest.toml @@ -3,7 +3,6 @@ run_top_level_functions = true [[test_rules]] selector = [ - "standalone/test_jbang_integration.py", "standalone/test_standalone.py"] per_test_timeout = 2400 partial_splits_individual_tests = true 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_abstract.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abstract.py index 70553a19ff..383ea8038b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abstract.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_abstract.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 @@ -90,6 +90,19 @@ def _reference_index(args): return result +def _reference_index_exact(args): + result = _reference_index(args) + result = result.__index__() + return type(result).__name__, result + + +def _reference_private_index(args): + if isinstance(args[0], int): + return type(args[0]).__name__, args[0] + result = _reference_index(args) + return type(result).__name__, result + + def _reference_asssize_t(args): v = args[0] err = args[1] @@ -237,6 +250,12 @@ def __index__(self): return 0xCAFE +class DummyIndexableIntSubclass(): + + def __index__(self): + return DummyIntSubclass() + + class DummyIntSubclass(int): def __int__(self): @@ -1006,9 +1025,10 @@ class TestAbstract(CPyExtTestCase): ) test_PyNumber_Index = CPyExtFunction( - _reference_index, + _reference_index_exact, lambda: ( (0,), + (True,), (1,), (-1,), (1.0,), @@ -1016,12 +1036,56 @@ class TestAbstract(CPyExtTestCase): (0x7FFFFFFF,), (0x7FFFFFFFFFFFFFFF,), (DummyIntable(),), + (DummyIndexableIntSubclass(),), (DummyIntSubclass(),), (NoNumber(),), ), resultspec="O", argspec='O', arguments=["PyObject* v"], + code=''' + PyObject* wrap_PyNumber_Index(PyObject* v) { + PyObject* result = PyNumber_Index(v); + if (result == NULL) { + return NULL; + } + PyObject* type_name = PyUnicode_FromString(Py_TYPE(result)->tp_name); + PyObject* tuple = PyTuple_Pack(2, type_name, result); + Py_DECREF(type_name); + Py_DECREF(result); + return tuple; + } + ''', + callfunction="wrap_PyNumber_Index", + cmpfunc=unhandled_error_compare + ) + + test__PyNumber_Index = CPyExtFunction( + _reference_private_index, + lambda: ( + (0,), + (True,), + (DummyIndexable(),), + (DummyIntSubclass(),), + (NoNumber(),), + ), + resultspec="O", + argspec='O', + arguments=["PyObject* v"], + code=''' + PyObject* wrap__PyNumber_Index(PyObject* v) { + PyObject* result = _PyNumber_Index(v); + if (result == NULL) { + return NULL; + } + PyObject* type_name = PyUnicode_FromString(Py_TYPE(result)->tp_name); + PyObject* tuple = PyTuple_Pack(2, type_name, result); + Py_DECREF(type_name); + Py_DECREF(result); + return tuple; + } + ''', + callfunction="wrap__PyNumber_Index", cmpfunc=unhandled_error_compare ) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_bytes.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_bytes.py index aabb6d6016..e1fcdd5706 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_bytes.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/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 @@ -39,7 +39,7 @@ import unittest from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, CPyExtType, \ - is_native_object + is_native_object, GRAALPYTHON def _reference_from_string_n(args): @@ -76,6 +76,13 @@ def _reference_format(args): fmt_args = tuple(args[1:]) return (fmt % fmt_args).encode() + +def _compare_resize_empty_to_nonempty(cresult, presult): + if GRAALPYTHON: + return cresult is None + return cresult is None or isinstance(cresult, SystemError) + + class CIter: def __iter__(self): return iter([1, 2, 3]) @@ -131,6 +138,116 @@ class TestPyBytes(CPyExtTestCase): arguments=["Py_ssize_t n"], ) + test_PyBytes_FromStringAndSize_empty = CPyExtFunction( + lambda args: 1, + lambda: ((),), + code=""" + static int CheckPyBytesFromStringAndSizeEmpty(void) { + PyObject *from_null = PyBytes_FromStringAndSize(NULL, 0); + PyObject *from_string = PyBytes_FromStringAndSize("ignored", 0); + PyObject *from_c_string = PyBytes_FromString(""); + PyObject *resized_to_empty = PyBytes_FromStringAndSize(NULL, 1); + int resized_to_empty_ok = _PyBytes_Resize(&resized_to_empty, 0) == 0; + int result = from_null != NULL && + from_string != NULL && + from_c_string != NULL && + resized_to_empty != NULL && + resized_to_empty_ok && + PyBytes_CheckExact(from_null) && + PyBytes_GET_SIZE(from_null) == 0; + result = result && + from_null == from_string && + from_null == from_c_string && + from_null == resized_to_empty; + Py_XDECREF(from_null); + Py_XDECREF(from_string); + Py_XDECREF(from_c_string); + Py_XDECREF(resized_to_empty); + return result; + } + """, + resultspec="i", + argspec="", + arguments=[], + callfunction="CheckPyBytesFromStringAndSizeEmpty", + ) + + test_PyBytes_one_byte_singletons = CPyExtFunction( + lambda args: 1, + lambda: ((),), + code=""" + static int CheckPyBytesOneByteSingletons(void) { + int all_from_size_cached = 1; + for (int i = 0; i < 256; i++) { + char ch = (char)i; + PyObject *a = PyBytes_FromStringAndSize(&ch, 1); + PyObject *b = PyBytes_FromStringAndSize(&ch, 1); + if (a == NULL || b == NULL || a != b || + PyBytes_GET_SIZE(a) != 1 || + ((unsigned char *)PyBytes_AS_STRING(a))[0] != (unsigned char)i) { + all_from_size_cached = 0; + } + Py_XDECREF(a); + Py_XDECREF(b); + } + + PyObject *from_string = PyBytes_FromString("x"); + PyObject *from_size = PyBytes_FromStringAndSize("x", 1); + PyObject *from_null = PyBytes_FromStringAndSize(NULL, 1); + PyObject *resize_cached = PyBytes_FromString("x"); + + int from_string_uses_cache = from_string != NULL && from_string == from_size; + int from_null_is_fresh = from_null != NULL && from_null != from_size; + int resize_cached_rejected = _PyBytes_Resize(&resize_cached, 0) < 0; + if (resize_cached_rejected) { + PyErr_Clear(); + } + int resize_fresh_ok = _PyBytes_Resize(&from_null, 0) == 0; + if (!resize_fresh_ok) { + PyErr_Clear(); + } + + int result = all_from_size_cached && + from_string_uses_cache && + from_null_is_fresh && + resize_cached_rejected && + resize_fresh_ok && + from_null != NULL && + PyBytes_GET_SIZE(from_null) == 0; + + Py_XDECREF(from_string); + Py_XDECREF(from_size); + Py_XDECREF(from_null); + Py_XDECREF(resize_cached); + return result; + } + """, + resultspec="i", + argspec="", + arguments=[], + callfunction="CheckPyBytesOneByteSingletons", + ) + + test_PyBytes_Resize_empty_to_nonempty = CPyExtFunction( + lambda args: None, + lambda: ((),), + code=""" + static PyObject *CheckPyBytesResizeEmptyToNonempty(void) { + PyObject *resized_from_empty = PyBytes_FromString(""); + if (_PyBytes_Resize(&resized_from_empty, 3) < 0) { + return NULL; + } + Py_XDECREF(resized_from_empty); + Py_RETURN_NONE; + } + """, + resultspec="O", + argspec="", + arguments=[], + callfunction="CheckPyBytesResizeEmptyToNonempty", + cmpfunc=_compare_resize_empty_to_nonempty, + ) + # PyBytes_FromString test_PyBytes_FromString = CPyExtFunction( lambda arg: bytes(arg[0], "utf-8"), diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_capi_logging.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_capi_logging.py new file mode 100644 index 0000000000..3b4a9e5ab5 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_capi_logging.py @@ -0,0 +1,125 @@ +# 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 os +import subprocess +import sys +import textwrap + +from . import GRAALPYTHON, compile_module_from_string + +if GRAALPYTHON: + import __graalpython__ + + +def test_private_log_handles_long_formatted_messages(): + if not GRAALPYTHON or not sys.executable: + return + + module = compile_module_from_string(""" + #define PY_SSIZE_T_CLEAN + #include + + #ifdef GRAALVM_PYTHON + #include + + #define GRAALPY_LOG_INFO 0x2 + + static void call_private_log_impl(const char *format, ...) { + va_list args; + va_start(args, format); + GraalPyTestCAPI->GraalPyPrivate_LogImpl(GRAALPY_LOG_INFO, format, args); + va_end(args); + } + #endif + + #define X10 "aaaaaaaaaa" + #define X100 X10 X10 X10 X10 X10 X10 X10 X10 X10 X10 + #define X1000 X100 X100 X100 X100 X100 X100 X100 X100 X100 X100 + #define LONG_LOG_MESSAGE X1000 X1000 + + static PyObject* trigger_long_log_message(PyObject* module, PyObject* unused) { + #ifdef GRAALVM_PYTHON + if (GraalPyTestCAPI_Import() < 0) { + return NULL; + } + call_private_log_impl("%s", LONG_LOG_MESSAGE); + #endif + Py_RETURN_NONE; + } + + static PyMethodDef module_methods[] = { + {"trigger_long_log_message", trigger_long_log_message, METH_NOARGS, ""}, + {NULL} + }; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, "graalpy_capi_log_overflow", "", -1, module_methods + }; + + PyMODINIT_FUNC PyInit_graalpy_capi_log_overflow(void) { + return PyModule_Create(&module_def); + } + """, "graalpy_capi_log_overflow") + + module_dir = os.path.dirname(module.__file__) + script = textwrap.dedent(f""" + import sys + sys.path.insert(0, {module_dir!r}) + import graalpy_capi_log_overflow + graalpy_capi_log_overflow.trigger_long_log_message() + print("done") + """) + args = [sys.executable, "--experimental-options=true", "--python.EnableDebuggingBuiltins"] + if not __graalpython__.is_native: + bytecode_dsl = str(__graalpython__.is_bytecode_dsl_interpreter).lower() + args.append(f"--vm.Dpython.EnableBytecodeDSLInterpreter={bytecode_dsl}") + + args += ["--log.python.capi.PythonCextBuiltins.level=INFO", "-c", script] + + proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if proc.returncode != 0: + stderr_tail = "\n".join(proc.stderr.splitlines()[-40:]) + message = ( + f"process exited with {proc.returncode}\n" + f"stdout:\n{proc.stdout}\n" + f"stderr tail:\n{stderr_tail}" + ) + raise AssertionError(message) + assert "done" in proc.stdout 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_classobject.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_classobject.py index ab854a9e3a..045fbdde36 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_classobject.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_classobject.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 @@ -91,3 +91,14 @@ def test_name_qualname(self): assert HeapTypeWithName.__name__ == 'HeapTypeWithNameRenamed' HeapTypeWithName.__qualname__ = 'foo.HeapTypeWithNameRenamed' assert HeapTypeWithName.__qualname__ == 'foo.HeapTypeWithNameRenamed' + + DottedModuleType = CPyExtHeapType( + 'DottedModuleType', + ready_code='spec.name = "pkg.mod.DottedModuleType";', + slots=[ + '{Py_tp_doc, (void *)"DottedModuleType($self, /)\\n--\\n\\nReturn dotted module type metadata."}', + ], + ) + assert DottedModuleType.__name__ == 'DottedModuleType' + assert DottedModuleType.__module__ == 'pkg.mod' + assert DottedModuleType.__doc__ == 'Return dotted module type metadata.' diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_codeobject.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_codeobject.py index a1962cbb30..ab78524174 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_codeobject.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_codeobject.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 @@ -119,7 +119,7 @@ class TestCodeobject(CPyExtTestCase): ], cmpfunc=lambda cr, pr: isinstance(cr, types.CodeType), ) - + test_PyCode_NewWithPosOnlyArgs = CPyExtFunction( lambda args: args, lambda: ( @@ -148,6 +148,82 @@ class TestCodeobject(CPyExtTestCase): cmpfunc=lambda cr, pr: isinstance(cr, types.CodeType), ) + test_PyCode_NewWithPosOnlyArgs_native_tuples = CPyExtFunction( + lambda args: args, + lambda: ( + tuple(), + ), + code=""" + static PyObject* make_tuple(const char *a, const char *b, const char *c) { + PyObject *tuple = PyTuple_New(3); + if (tuple == NULL) { + return NULL; + } + PyObject *item0 = PyUnicode_FromString(a); + PyObject *item1 = PyUnicode_FromString(b); + PyObject *item2 = PyUnicode_FromString(c); + if (item0 == NULL || item1 == NULL || item2 == NULL) { + Py_XDECREF(item0); + Py_XDECREF(item1); + Py_XDECREF(item2); + Py_DECREF(tuple); + return NULL; + } + PyTuple_SET_ITEM(tuple, 0, item0); + PyTuple_SET_ITEM(tuple, 1, item1); + PyTuple_SET_ITEM(tuple, 2, item2); + return tuple; + } + + static PyCodeObject* wrap_PyCode_NewWithPosOnlyArgs_native_tuples(PyObject* ignored) { + PyObject *code = PyBytes_FromStringAndSize("", 0); + PyObject *consts = PyTuple_New(0); + PyObject *names = PyTuple_New(0); + PyObject *varnames = make_tuple("a", "b", "c"); + PyObject *freevars = PyTuple_New(0); + PyObject *cellvars = PyTuple_New(0); + PyObject *filename = PyUnicode_FromString("filename"); + PyObject *name = PyUnicode_FromString("name"); + PyObject *qualname = PyUnicode_FromString("module.name"); + PyObject *linetable = PyBytes_FromStringAndSize("", 0); + PyObject *exceptiontable = PyBytes_FromStringAndSize("", 0); + PyCodeObject *result = NULL; + + if (code == NULL || consts == NULL || names == NULL || varnames == NULL || + freevars == NULL || cellvars == NULL || filename == NULL || name == NULL || + qualname == NULL || linetable == NULL || exceptiontable == NULL) { + goto done; + } + result = PyUnstable_Code_NewWithPosOnlyArgs( + 1, 0, 2, 3, 4, 0, + code, consts, names, varnames, + freevars, cellvars, + filename, name, qualname, + 1, linetable, exceptiontable); + + done: + Py_XDECREF(code); + Py_XDECREF(consts); + Py_XDECREF(names); + Py_XDECREF(varnames); + Py_XDECREF(freevars); + Py_XDECREF(cellvars); + Py_XDECREF(filename); + Py_XDECREF(name); + Py_XDECREF(qualname); + Py_XDECREF(linetable); + Py_XDECREF(exceptiontable); + return result; + } + """, + resultspec="O", + resulttype="PyCodeObject*", + argspec="", + arguments=["PyObject* ignored"], + callfunction="wrap_PyCode_NewWithPosOnlyArgs_native_tuples", + cmpfunc=lambda cr, pr: isinstance(cr, types.CodeType), + ) + test_PyCode_Addr2Line = CPyExtFunction( reference_PyCode_Addr2Line, lambda: ( diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ctypes.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ctypes.py index b3f47b6573..7ce23fd9db 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ctypes.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ctypes.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 @@ -128,7 +128,6 @@ def test_buffer(self): assert struct.Struct(buffer.format).size == int_format.size assert buffer.shape == (2, 2) -# TODO: GR-60735, we cannot support this without NFI struct by value support def ignore_test_custom_libs(): # 16B: returned in registers on System V AMD64 ABI class MySmallStruct1(ctypes.Structure): 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..b533fd1ad0 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 @@ -85,6 +85,11 @@ def _reference_set_item(args): raise SystemError +def _reference_setdefault(args): + d, key, default = args + return d.setdefault(key, default), d + + def _reference_del_item(args): d = args[0] del d[args[1]] @@ -307,7 +312,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; @@ -360,6 +365,30 @@ class TestPyDict(CPyExtTestCase): callfunction="wrap__PyDict_SetItem_KnownHash", ) + # PyDict_SetDefault + test_PyDict_SetDefault = CPyExtFunction( + _reference_setdefault, + lambda: ( + ({}, 1, 2), + ({1: 3}, 1, 2), + ({}, "a", "hello"), + ({"a": "existing"}, "a", "default"), + ), + code='''PyObject* wrap_PyDict_SetDefault(PyObject* dict, PyObject* key, PyObject* deflt) { + PyObject* result = PyDict_SetDefault(dict, key, deflt); + if (result == NULL) { + return NULL; + } + Py_INCREF(result); + return Py_BuildValue("NN", result, Py_NewRef(dict)); + }''', + resultspec="O", + argspec='OOO', + arguments=("PyObject* dict", "PyObject* key", "PyObject* deflt"), + callfunction="wrap_PyDict_SetDefault", + cmpfunc=unhandled_error_compare + ) + # PyDict_Size test_PyDict_Size = CPyExtFunction( lambda args: len(args[0]), diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_err.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_err.py index 4e0d68e986..0479703fe7 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_err.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_err.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 @@ -42,7 +42,7 @@ import warnings from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionVoid, unhandled_error_compare, \ - CPyExtType, is_native_object + CPyExtHeapType, CPyExtType, is_native_object def _reference_setstring(args): @@ -737,6 +737,37 @@ def test_init(self): assert type(e) == ExceptionSubclass assert isinstance(e, Exception) + def test_syntax_error_subclass(self): + NativeSyntaxErrorSubclass = CPyExtHeapType( + "NativeSyntaxErrorSubclass", + bases=(SyntaxError,), + struct_base="PySyntaxErrorObject base;", + ) + assert SyntaxError.__basicsize__ > BaseException.__basicsize__ + assert NativeSyntaxErrorSubclass.__basicsize__ >= SyntaxError.__basicsize__ + e = NativeSyntaxErrorSubclass("bad", ("pkg/module.py", 12, 4, "x = 1", 12, 9)) + assert is_native_object(e) + assert type(e) is NativeSyntaxErrorSubclass + assert isinstance(e, SyntaxError) + assert e.args == ("bad", ("pkg/module.py", 12, 4, "x = 1", 12, 9)) + assert e.msg == "bad" + assert e.filename == "pkg/module.py" + assert e.lineno == 12 + assert e.offset == 4 + assert e.text == "x = 1" + assert e.end_lineno == 12 + assert e.end_offset == 9 + assert e.print_file_and_line is None + assert str(e) == "bad (module.py, line 12)" + + e.filename = "other.py" + assert e.filename == "other.py" + assert str(e) == "bad (other.py, line 12)" + + del e.lineno + assert e.lineno is None + assert str(e) == "bad (other.py)" + def test_managed_subtype(self): class ManagedSubclass(ExceptionSubclass): pass diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_float.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_float.py index 6f4b3cb978..9cfa28cb31 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_float.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_float.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 @@ -245,6 +245,25 @@ class ManagedSubclass(NativeFloatSubclass): f = ManagedSubclass(1.0) assert is_native_object(f) + def test_alloc_failure(self): + """A native float subtype must propagate tp_alloc failure instead of crashing on NULL.""" + FailingAllocFloatSubclass = CPyExtType( + 'FailingAllocFloatSubclass', + r''' + static PyObject* fail_alloc(PyTypeObject *type, Py_ssize_t nitems) { + PyErr_NoMemory(); + return NULL; + } + ''', + struct_base='PyFloatObject base;', + tp_base='&PyFloat_Type', + tp_new='0', + tp_alloc='fail_alloc', + tp_free='0', + ) + with self.assertRaises(MemoryError): + FailingAllocFloatSubclass(1.0) + def test_methods(self): f = NativeFloatSubclass(1.1) zero = NativeFloatSubclass(0.0) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_functions.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_functions.py index 8cf584359b..8a4a80c3c2 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_functions.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_functions.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 @@ -41,7 +41,14 @@ import sys import unittest -from . import CPyExtType, CPyExtTestCase, CPyExtFunction, unhandled_error_compare, CPyExtHeapType +from . import ( + CPyExtFunction, + CPyExtHeapType, + CPyExtTestCase, + CPyExtType, + compile_module_from_string, + unhandled_error_compare, +) DIR = os.path.dirname(__file__) @@ -563,6 +570,113 @@ def _ref_hash_not_implemented(args): # test calling m_meth class TestPyCFunction(unittest.TestCase): + def test_docstring_text_signature(self): + module = compile_module_from_string(r""" + #define PY_SSIZE_T_CLEAN + #include + + static PyObject *with_signature(PyObject *self, PyObject *arg) { + Py_RETURN_NONE; + } + + static PyObject *without_signature(PyObject *self, PyObject *arg) { + Py_RETURN_NONE; + } + + static PyObject *without_doc(PyObject *self, PyObject *unused) { + Py_RETURN_NONE; + } + + static PyMethodDef module_methods[] = { + {"with_signature", with_signature, METH_O, + "with_signature($module, value, /)\n" + "--\n\n" + "Return module function metadata."}, + {"without_signature", without_signature, METH_O, "Return a plain docstring."}, + {"without_doc", without_doc, METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} + }; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "textsignature", + "", + -1, + module_methods, + NULL, NULL, NULL, NULL + }; + + PyMODINIT_FUNC + PyInit_textsignature(void) + { + return PyModule_Create(&module_def); + } + """, "textsignature") + + assert module.with_signature.__doc__ == "Return module function metadata." + assert module.with_signature.__text_signature__ == "($module, value, /)" + assert module.without_signature.__doc__ == "Return a plain docstring." + assert module.without_signature.__text_signature__ is None + assert module.without_doc.__doc__ is None + assert module.without_doc.__text_signature__ is None + + TypeWithTextSignature = CPyExtType( + "TypeWithTextSignature", + """ + static PyObject *method_with_signature(PyObject *self, PyObject *arg) { + Py_RETURN_NONE; + } + """, + tp_methods=""" + {"method_with_signature", method_with_signature, METH_O, + "method_with_signature($self, value, /)\\n" + "--\\n\\n" + "Return type method metadata."} + """, + ) + assert TypeWithTextSignature.method_with_signature.__doc__ == "Return type method metadata." + assert TypeWithTextSignature.method_with_signature.__text_signature__ == "($self, value, /)" + + test_PyCFunction_NewEx_non_string_module = CPyExtFunction( + lambda args: 1, + lambda: ( + ({},), + ([],), + (object(),), + ), + code=""" + static PyObject *native_meth_noargs(PyObject *self, PyObject *dummy) { + Py_RETURN_NONE; + } + + static PyMethodDef non_string_module_method = { + "non_string_module_method", + native_meth_noargs, + METH_NOARGS, + NULL + }; + + static int wrap_PyCFunction_NewEx_non_string_module(PyObject *module_obj) { + PyObject *func = PyCFunction_NewEx(&non_string_module_method, Py_None, module_obj); + if (func == NULL) { + return -1; + } + PyObject *attr = PyObject_GetAttrString(func, "__module__"); + Py_DECREF(func); + if (attr == NULL) { + return -1; + } + int same = attr == module_obj; + Py_DECREF(attr); + return same; + } + """, + resultspec="i", + argspec="O", + arguments=["PyObject* module_obj"], + callfunction="wrap_PyCFunction_NewEx_non_string_module", + ) + def test_PyMethodDef(self): TestPyMethodDef = CPyExtType( "TestPyMethodDef", 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..3686d3de21 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,104 @@ 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 + #include + #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 (GraalPyTestCAPI->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 + GraalPyTestCAPI->EnableReferenceQueuePolling(); + polling_disabled = 0; + GraalPyTestCAPI->TriggerGC(0); + #endif + Py_RETURN_NONE; + + error: + #if GRAALVM_PYTHON + if (polling_disabled) { + GraalPyTestCAPI->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, ""} + """, + ready_code=""" + #if GRAALVM_PYTHON + if (GraalPyTestCAPI_Import() < 0) { + return NULL; + } + #endif + """, + tp_dealloc='(destructor)gtd_dealloc', +) + class TestGC1(unittest.TestCase): def test_native_class(self): @@ -222,6 +320,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..202449d507 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, 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,9 +37,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import gc import sys -from . import CPyExtTestCase, CPyExtFunction, unhandled_error_compare +from . import CPyExtTestCase, CPyExtFunction, compile_module_from_string, unhandled_error_compare def _reference_new_list(args): @@ -122,6 +123,47 @@ class DummyListSubclass(list): class TestPyList(CPyExtTestCase): + def test_clear_native_storage_gc(self): + module = compile_module_from_string( + """ + #define PY_SSIZE_T_CLEAN + #include + + static PyObject* clear_native_list_storage(PyObject* self, PyObject* list) { + if (!PyList_Check(list) || PyList_GET_SIZE(list) == 0) { + PyErr_SetString(PyExc_ValueError, "expected non-empty list"); + return NULL; + } + // Force storage to native + PySequence_Fast_ITEMS(list); + if (Py_TYPE(list)->tp_clear((PyObject *) list) < 0) { + return NULL; + } + Py_RETURN_NONE; + } + + static PyMethodDef methods[] = { + {"clear_native_list_storage", (PyCFunction) clear_native_list_storage, METH_O, NULL}, + {NULL, NULL, 0, NULL} + }; + + static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, "test_list_native_storage_gc", NULL, -1, methods + }; + + PyMODINIT_FUNC PyInit_test_list_native_storage_gc(void) { + return PyModule_Create(&module); + } + """, + "test_list_native_storage_gc", + ) + + items = [object()] + module.clear_native_list_storage(items) + del items + for _ in range(5): + gc.collect() + test_PyList_New = CPyExtFunction( _reference_new_list, lambda: ( @@ -167,36 +209,64 @@ class TestPyList(CPyExtTestCase): test_PyList_SetItem = CPyExtFunction( _reference_setitem, lambda: ( - (4, 0, 0), - (4, 3, 5), + (4, 0, 0, False), + (4, 3, 5, False), + (4, 0, 0, True), + (4, 3, 5, True), ), - code='''PyObject* wrap_PyList_SetItem(Py_ssize_t capacity, Py_ssize_t idx, PyObject* new_item) { + code='''PyObject* wrap_PyList_SetItem(Py_ssize_t capacity, Py_ssize_t idx, PyObject* new_item, int init_with_none) { PyObject *newList = PyList_New(capacity); Py_ssize_t i; for (i = 0; i < capacity; i++) { - if (i == idx) { - Py_INCREF(new_item); - PyList_SetItem(newList, i, new_item); - } else { + if (init_with_none || i != idx) { Py_INCREF(Py_None); PyList_SetItem(newList, i, Py_None); } } + Py_INCREF(new_item); + PyList_SetItem(newList, idx, new_item); return newList; } ''', resultspec="O", - argspec='nnO', - arguments=["Py_ssize_t capacity", "Py_ssize_t size", "PyObject* new_item"], + argspec='nnOp', + arguments=["Py_ssize_t capacity", "Py_ssize_t size", "PyObject* new_item", "int init_with_none"], callfunction="wrap_PyList_SetItem", cmpfunc=unhandled_error_compare ) + test_native_list_setitem_decrefs_none = CPyExtFunction( + lambda args: [args[0]], + lambda: ( + ("new item",), + ), + code='''PyObject* wrap_native_list_setitem_decrefs_none(PyObject* new_item) { + PyObject *list = PyList_New(1); + if (list == NULL) { + return NULL; + } + Py_INCREF(Py_None); + PyList_SET_ITEM(list, 0, Py_None); + if (PySequence_SetItem(list, 0, new_item) < 0) { + Py_DECREF(list); + return NULL; + } + return list; + } + ''', + resultspec="O", + argspec='O', + arguments=["PyObject* new_item"], + callfunction="wrap_native_list_setitem_decrefs_none", + cmpfunc=unhandled_error_compare + ) + test_PyList_SET_ITEM = CPyExtFunction( _wrap_list_fun(_reference_SET_ITEM), lambda: ( ([1,2,3,4], 0, _reference_SET_ITEM), ([1,2,3,4], 3, _reference_SET_ITEM), + ([None], 0, 0), ), code='''PyObject* wrap_PyList_SET_ITEM(PyObject* op, Py_ssize_t idx, PyObject* newitem) { Py_INCREF(newitem); @@ -276,11 +346,11 @@ class TestPyList(CPyExtTestCase): arguments=["PyObject* op", "Py_ssize_t ilow", "Py_ssize_t ihigh"], cmpfunc=unhandled_error_compare ) - + test_PyList_SetSlice = CPyExtFunction( _reference_setslice, lambda: ( - ([1,2,3,4],0,4,[5,6,7,8]), + ([1,2,3,4],0,4,[5,6,7,8]), ([],1,2, [5,6]), ([1,2,3,4],10,20,[5,6,7,8]), (DummyClass(),10,20, [1]), @@ -312,9 +382,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', @@ -325,16 +393,16 @@ class TestPyList(CPyExtTestCase): test_PyList_Check = CPyExtFunction( lambda args: isinstance(args[0], list), lambda: ( - ([1,2,3,4],), - ([None],), - ([],), - (list(),), - (dict(),), - (tuple(),), - (DummyListSubclass(),), - (DummyClass(),), - (1,), - (1.0,), + ([1,2,3,4],), + ([None],), + ([],), + (list(),), + (dict(),), + (tuple(),), + (DummyListSubclass(),), + (DummyClass(),), + (1,), + (1.0,), ), resultspec="i", argspec='O', @@ -345,16 +413,16 @@ class TestPyList(CPyExtTestCase): test_PyList_CheckExact = CPyExtFunction( lambda args: type(args[0]) is list, lambda: ( - ([1,2,3,4],), - ([None],), - ([],), - (list(),), - (dict(),), - (tuple(),), - (DummyListSubclass(),), - (DummyClass(),), - (1,), - (1.0,), + ([1,2,3,4],), + ([None],), + ([],), + (list(),), + (dict(),), + (tuple(),), + (DummyListSubclass(),), + (DummyClass(),), + (1,), + (1.0,), ), resultspec="i", argspec='O', diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_long.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_long.py index cc5d718ca3..3c3ad95443 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_long.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_long.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 @@ -38,8 +38,7 @@ # SOFTWARE. import struct -from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare - +from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, CPyExtType, unhandled_error_compare int_bits = struct.calcsize('i') * 8 max_int = 2 ** (int_bits - 1) - 1 @@ -48,6 +47,10 @@ max_long = 2 ** (long_bits - 1) - 1 min_long = -2 ** (long_bits - 1) max_ulong = 2 ** long_bits +ulonglong_bits = struct.calcsize('Q') * 8 +max_ulonglong = 2 ** ulonglong_bits +size_t_bits = struct.calcsize('P') * 8 +max_size_t = 2 ** size_t_bits ssize_t_bits = struct.calcsize('n') * 8 max_ssize_t = 2 ** (ssize_t_bits - 1) - 1 min_ssize_t = -2 ** (ssize_t_bits - 1) @@ -113,6 +116,15 @@ def _reference_fromvoidptr(args): return n +def _reference_asvoidptr_roundtrip(args): + n = args[0] + if n < min_long or n >= max_ulonglong: + raise OverflowError("Python int too large to convert") + if n < 0: + return _reference_fromvoidptr(args) + return n + + def _reference_fromlong(args): n = args[0] return n @@ -133,6 +145,22 @@ def _reference_is_compact(args): return 1 if -2147483648 <= n <= 2147483647 else 0 +def _reference_long_from_string(args): + num, base = args + if not num.strip(): + return ValueError, len(num) + try: + return int(num, base), len(num) + except Exception as e: + for i in range(len(num)): + try: + int(num[:i + 1], base) + except Exception: + if num[0] == '0' and base == 0: + i = len(num) + return type(e), i + + class DummyNonInt(): pass @@ -167,6 +195,28 @@ def _int_examples(): class TestPyLong(CPyExtTestCase): + def test_native_long_subtype_has_native_layout(self): + NativeLongWithMember = CPyExtType( + "NativeLongWithMember", + """ + static void NativeLongWithMember_dealloc(NativeLongWithMemberObject *self) { + Py_XDECREF(self->member); + Py_TYPE(self)->tp_free((PyObject *)self); + } + """, + ready_code="NativeLongWithMemberType.tp_new = PyLong_Type.tp_new;", + tp_base="&PyLong_Type", + struct_base="PyLongObject base;", + cmembers="PyObject *member;", + tp_dealloc="(destructor)NativeLongWithMember_dealloc", + tp_members='{"member", T_OBJECT_EX, offsetof(NativeLongWithMemberObject, member), 0, NULL}', + ) + + obj = NativeLongWithMember(10) + assert obj == 10 + obj.member = "foo" + assert obj.member == "foo" + test_PyLong_AsLong = CPyExtFunction( _reference_as_long, _int_examples, @@ -228,16 +278,90 @@ class TestPyLong(CPyExtTestCase): cmpfunc=unhandled_error_compare ) + test_PyLong_FromUnsignedLong = CPyExtFunction( + lambda args: (0, 1, max_ulong - 1), + lambda: ((),), + code=""" + PyObject* wrap_PyLong_FromUnsignedLong() { + PyObject* small = PyLong_FromUnsignedLong(0); + PyObject* one = PyLong_FromUnsignedLong(1); + PyObject* large = PyLong_FromUnsignedLong(ULONG_MAX); + PyObject* result; + if (small == NULL || one == NULL || large == NULL) { + Py_XDECREF(small); + Py_XDECREF(one); + Py_XDECREF(large); + return NULL; + } + result = PyTuple_Pack(3, small, one, large); + Py_DECREF(small); + Py_DECREF(one); + Py_DECREF(large); + return result; + } + """, + resultspec="O", + argspec='', + arguments=[], + callfunction="wrap_PyLong_FromUnsignedLong", + cmpfunc=unhandled_error_compare + ) + + test_PyLong_FromUnsignedLongLong = CPyExtFunction( + lambda args: (0, 1, max_ulonglong - 1), + lambda: ((),), + code=""" + PyObject* wrap_PyLong_FromUnsignedLongLong() { + PyObject* small = PyLong_FromUnsignedLongLong(0); + PyObject* one = PyLong_FromUnsignedLongLong(1); + PyObject* large = PyLong_FromUnsignedLongLong(ULLONG_MAX); + PyObject* result; + if (small == NULL || one == NULL || large == NULL) { + Py_XDECREF(small); + Py_XDECREF(one); + Py_XDECREF(large); + return NULL; + } + result = PyTuple_Pack(3, small, one, large); + Py_DECREF(small); + Py_DECREF(one); + Py_DECREF(large); + return result; + } + """, + resultspec="O", + argspec='', + arguments=[], + callfunction="wrap_PyLong_FromUnsignedLongLong", + cmpfunc=unhandled_error_compare + ) + test_PyLong_FromSize_t = CPyExtFunction( - lambda args: int(args[0]), - lambda: ( - (0,), - (1,), - (0xffffffff,), - ), + lambda args: (0, 1, max_size_t - 1), + lambda: ((),), + code=""" + PyObject* wrap_PyLong_FromSize_t() { + PyObject* small = PyLong_FromSize_t(0); + PyObject* one = PyLong_FromSize_t(1); + PyObject* large = PyLong_FromSize_t((size_t)-1); + PyObject* result; + if (small == NULL || one == NULL || large == NULL) { + Py_XDECREF(small); + Py_XDECREF(one); + Py_XDECREF(large); + return NULL; + } + result = PyTuple_Pack(3, small, one, large); + Py_DECREF(small); + Py_DECREF(one); + Py_DECREF(large); + return result; + } + """, resultspec="O", - argspec='n', - arguments=["size_t n"], + argspec='', + arguments=[], + callfunction="wrap_PyLong_FromSize_t", cmpfunc=unhandled_error_compare ) @@ -247,6 +371,7 @@ class TestPyLong(CPyExtTestCase): (0.0,), (-1.0,), (-11.123456789123456789,), + (1.0e100,), ), resultspec="O", argspec='d', @@ -269,6 +394,31 @@ class TestPyLong(CPyExtTestCase): cmpfunc=unhandled_error_compare ) + test_PyLong_AsVoidPtr = CPyExtFunction( + _reference_asvoidptr_roundtrip, + lambda: ( + (0,), + (42,), + (-1,), + (0xffffffff,), + (0xffffffffffffffff,), + (0x10000000000000000,), + ), + code="""PyObject* wrap_PyLong_AsVoidPtr(PyObject* obj) { + void* ptr = PyLong_AsVoidPtr(obj); + if (ptr == NULL && PyErr_Occurred()) { + return NULL; + } + return PyLong_FromVoidPtr(ptr); + } + """, + resultspec="O", + argspec='O', + arguments=["PyObject* obj"], + callfunction="wrap_PyLong_AsVoidPtr", + cmpfunc=unhandled_error_compare + ) + test_PyLong_FromVoidPtrAllocated = CPyExtFunction( lambda args: int, lambda: ((None,),), @@ -360,10 +510,11 @@ class TestPyLong(CPyExtTestCase): ) test_PyLong_FromString = CPyExtFunction( - lambda args: int(args[0], args[1]), + _reference_long_from_string, lambda: ( ("00", 0), ("03", 0), + ("0003", 0), (" 12 ", 10), (" 12abg13 ", 22), ("12", 0), @@ -371,10 +522,27 @@ class TestPyLong(CPyExtTestCase): ("0x132f1", 0), ("0x132132ff213213213231", 0), ("13123441234123423412341234123412341234124312341234213213213213213231", 0), + ("1312344123412342341234123412341234123x4124312341234213213213213213231", 0), + ("", 0), + (" ", 0), + ("123 ", 0), + ("-123 ", 0), + ("123 x", 0), + ("x", 0), + ("_1", 0), + ("1_", 0), + ("1_1", 0), + ("1__1", 0), ), code='''PyObject* wrap_PyLong_FromString(const char* str, int base) { char* pend; - return PyLong_FromString(str, &pend, base); + PyObject* val = PyLong_FromString(str, &pend, base); + if (!val) { + PyObject* exc = PyErr_GetRaisedException(); + val = Py_NewRef(Py_TYPE(exc)); + Py_DECREF(exc); + } + return Py_BuildValue("OL", val, pend - str); }''', callfunction="wrap_PyLong_FromString", resultspec="O", diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_member.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_member.py index c2c1c2a708..1d17ba9ae6 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_member.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_member.py @@ -1,4 +1,4 @@ -# 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 @@ -101,6 +101,22 @@ def test_member(self): return Py_None; } + PyObject* set_string_inplace(PyObject *self, PyObject *arg) { + TestMemberObject *tmo = (TestMemberObject *)self; + Py_ssize_t len; + const char *utf8 = PyUnicode_AsUTF8AndSize(arg, &len); + if (utf8 == NULL) + return NULL; + if (len >= (Py_ssize_t)sizeof(tmo->member_string_inplace)) { + PyErr_SetString(PyExc_ValueError, "string too long"); + return NULL; + } + memcpy(tmo->member_string_inplace, utf8, len); + tmo->member_string_inplace[len] = 0; + Py_INCREF(Py_None); + return Py_None; + } + PyObject* get_min_values(PyObject *self) { PyObject *result = PyTuple_New(9); PyTuple_SetItem(result, 0, PyLong_FromSsize_t(CHAR_MIN)); @@ -138,6 +154,7 @@ def test_member(self): float member_float; double member_double; char *member_string; + char member_string_inplace[8]; char member_char; char member_byte; unsigned char member_ubyte; @@ -159,6 +176,7 @@ def test_member(self): {"member_float", T_FLOAT, offsetof(TestMemberObject, member_float), 0, "float member"}, {"member_double", T_DOUBLE, offsetof(TestMemberObject, member_double), 0, "double member"}, {"member_string", T_STRING, offsetof(TestMemberObject, member_string), 0, "string member"}, + {"member_string_inplace", T_STRING_INPLACE, offsetof(TestMemberObject, member_string_inplace), 0, "string inplace member"}, {"member_char", T_CHAR, offsetof(TestMemberObject, member_char), 0, "char member"}, {"member_byte", T_BYTE, offsetof(TestMemberObject, member_byte), 0, "byte member"}, {"member_ubyte", T_UBYTE, offsetof(TestMemberObject, member_ubyte), 0, "ubyte member"}, @@ -172,6 +190,7 @@ def test_member(self): """, tp_methods=''' {"set_string", (PyCFunction)set_string, METH_O, ""}, + {"set_string_inplace", (PyCFunction)set_string_inplace, METH_O, ""}, {"get_min_values", (PyCFunction)get_min_values, METH_NOARGS, ""}, {"get_max_values", (PyCFunction)get_max_values, METH_NOARGS, ""} ''', @@ -277,6 +296,19 @@ def test_member(self): assert type(obj.member_string) is str assert obj.member_string == "hello" + # T_STRING_INPLACE + assert type(obj.member_string_inplace) is str + assert obj.member_string_inplace == "" + assert_raises(TypeError, delattr, obj, "member_string_inplace") + assert_raises(TypeError, setattr, obj, "member_string_inplace", "hello") + obj.set_string_inplace("hello") + assert type(obj.member_string_inplace) is str + assert obj.member_string_inplace == "hello" + obj.set_string_inplace("hi") + assert obj.member_string_inplace == "hi" + assert_raises(ValueError, obj.set_string_inplace, "too long") + assert obj.member_string_inplace == "hi" + # T_CHAR assert type(obj.member_char) is str assert obj.member_char == "\x00", "was: %r" % obj.member_char diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_memoryview_fromobject_error.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_memoryview_fromobject_error.py new file mode 100644 index 0000000000..cf5c4957eb --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_memoryview_fromobject_error.py @@ -0,0 +1,116 @@ +# 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 os +import subprocess +import sys +import textwrap +import unittest +from pathlib import Path +from unittest import skipIf + +from . import GRAALPYTHON + + +@skipIf(not GRAALPYTHON, "requires GraalPy native memory accounting") +class TestPyMemoryViewFromObjectErrorPath(unittest.TestCase): + def assert_script_succeeds(self, script): + # Run each check in a fresh GraalPy process. The RSS assertion is otherwise too noisy because + # the surrounding graalpytest JVM can grow for reasons unrelated to this native Py_buffer path. + script = textwrap.dedent(f""" + import sys + + sys.path.insert(0, {str(Path(__file__).parents[2])!r}) + """) + textwrap.dedent(script) + env = os.environ.copy() + env["PYTHONHASHSEED"] = "0" + process = subprocess.run( + # CPyExtType imports require debugging builtins in this nested GraalPy process as well. + [sys.executable, "--experimental-options", "--python.EnableDebuggingBuiltins", "-c", script], + capture_output=True, + env=env, + text=True, + ) + self.assertEqual(process.returncode, 0, process.stdout + process.stderr) + + def test_construction_failure_releases_acquired_buffer(self): + self.assert_script_succeeds(r""" + from tests.cpyext import CPyExtType + + ReleaseOnFailureType = CPyExtType( + "TestMemoryViewReleaseOnConstructionFailure", + r''' + static int release_count = 0; + static char buf[] = {42}; + + static int getbuffer(TestMemoryViewReleaseOnConstructionFailureObject *self, Py_buffer *view, int flags) { + // Use a length that is valid for Py_buffer but too large for GraalPy's memoryview shape. + // This makes PyObject_GetBuffer succeed and memoryview construction fail afterwards. + return PyBuffer_FillInfo(view, (PyObject*)self, buf, PY_SSIZE_T_MAX, 1, flags); + } + + static void releasebuffer(TestMemoryViewReleaseOnConstructionFailureObject *self, Py_buffer *view) { + release_count++; + } + + static PyObject* get_release_count(PyObject* self, PyObject* args) { + return PyLong_FromLong(release_count); + } + + static PyBufferProcs as_buffer = { + (getbufferproc)getbuffer, + (releasebufferproc)releasebuffer, + }; + ''', + tp_as_buffer='&as_buffer', + tp_methods='{"get_release_count", get_release_count, METH_NOARGS, ""}', + ) + + obj = ReleaseOnFailureType() + try: + memoryview(obj) + except Exception: + pass + else: + raise AssertionError("memoryview unexpectedly succeeded") + + release_count = obj.get_release_count() + if release_count != 1: + raise AssertionError(f"expected one releasebuffer call, got {release_count}") + """) 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..79ff655b9b 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 @@ -136,6 +138,8 @@ class TestMethodsSubclass(TestMethods): assert obj.meth_varargs_keywords(1, 2, 3, a=1, b=2) == (obj, (1, 2, 3), {'a': 1, 'b': 2}) assert obj.meth_fastcall(1, 2, 3) == (obj, (1, 2, 3)) assert obj.meth_fastcall_keywords(1, 2, 3, a=1, b=2) == (obj, (1, 2, 3), {'a': 1, 'b': 2}) + # Repeat to exercise the eager native kwnames tuple path after storage profiling. + assert obj.meth_fastcall_keywords(1, 2, 3, a=1, b=2) == (obj, (1, 2, 3), {'a': 1, 'b': 2}) assert obj.meth_method(1, 2, 3, a=1, b=2) == (obj, TestMethods, (1, 2, 3), {'a': 1, 'b': 2}) # class methods assert obj.meth_class_o(1) == (TestMethodsSubclass, 1) @@ -170,6 +174,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_misc.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_misc.py index 5af01ef48e..7d0510cc3b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_misc.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_misc.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 @@ -47,6 +47,34 @@ __global_builtins_dict = builtins.__dict__ +FSPathUnicodeSubclass = CPyExtType( + "FSPathUnicodeSubclass", + '', + struct_base='PyUnicodeObject base;', + tp_base='&PyUnicode_Type', + tp_new='0', + tp_alloc='0', + tp_free='0', +) + +FSPathBytesSubclass = CPyExtType( + "FSPathBytesSubclass", + '', + struct_base='PyBytesObject base;', + tp_itemsize='sizeof(char)', + tp_base='&PyBytes_Type', + tp_new='0', + tp_alloc='0', + tp_free='0', +) + +class FSPathWrapper: + def __init__(self, value): + self.value = value + + def __fspath__(self): + return self.value + def _reference_importmodule(args): return __import__(args[0], fromlist=["*"]) @@ -97,6 +125,31 @@ class TestMisc(CPyExtTestCase): arguments=["PyObject* ellipsis_singleton"], ) + test_empty_tuple_user = CPyExtFunction( + lambda args: 1, + lambda: ( + tuple(), + ), + code=""" + static int CheckEmptyTupleUser(PyObject* ignored) { + PyObject *tuple_empty = PyTuple_New(0); + int result = tuple_empty != NULL && + PyTuple_CheckExact(tuple_empty) && + PyTuple_GET_SIZE(tuple_empty) == 0; + Py_XDECREF(tuple_empty); + return result; + } + """, + resultspec="i", + argspec="", + arguments=["PyObject* ignored"], + callfunction="CheckEmptyTupleUser", + ) + + def test_sre_bytes_empty_user(self): + import re + self.assertEqual(re.sub(b"a", b"", b"banana"), b"bnn") + test_PyImport_ImportModule = CPyExtFunction( _reference_importmodule, lambda: ( @@ -223,11 +276,19 @@ class TestMisc(CPyExtTestCase): ), code=""" #ifdef GRAALVM_PYTHON - // internal function defined in 'capi.c' - int GraalPyPrivate_ToNative(void *); + #include + + static int call_GraalPyPrivate_ToNative(void *arg) { + if (GraalPyTestCAPI == NULL) { + if (GraalPyTestCAPI_Import() < 0) { + return -1; + } + } + return GraalPyTestCAPI->ToNative(arg); + } #else // nothing to do on CPython - static inline int GraalPyPrivate_ToNative(void *arg) { + static inline int call_GraalPyPrivate_ToNative(void *arg) { return 0; } #endif @@ -240,14 +301,21 @@ class TestMisc(CPyExtTestCase): double dval = PyFloat_AsDouble(val); - if (GraalPyPrivate_ToNative(val)) { + if (call_GraalPyPrivate_ToNative(val)) { + if (PyErr_Occurred()) { + return NULL; + } return Py_False; } // a fresh object with the same value PyObject *val1 = PyFloat_FromDouble(dval); - if (GraalPyPrivate_ToNative(val1)) { + if (call_GraalPyPrivate_ToNative(val1)) { + if (PyErr_Occurred()) { + Py_DECREF(val1); + return NULL; + } return Py_False; } @@ -326,6 +394,8 @@ class TestMisc(CPyExtTestCase): (b"bytespath",), ("stringpath",), (pathlib.Path("pathpath"),), + (FSPathWrapper(FSPathUnicodeSubclass("native-stringpath")),), + (FSPathWrapper(FSPathBytesSubclass(b"native-bytespath")),), (123,), (object(),), ), @@ -341,6 +411,37 @@ class TestMisc(CPyExtTestCase): cmpfunc=unhandled_error_compare ) + test_PyOS_FSPath_native_subclass = CPyExtFunction( + lambda args: 1, + lambda: ( + (FSPathUnicodeSubclass("stringpath"),), + (FSPathBytesSubclass(b"bytespath"),), + ), + callfunction="call_PyOS_FSPath_native_subclass", + code=""" + static int call_PyOS_FSPath_native_subclass(PyObject* value) { + PyObject* result = PyOS_FSPath(value); + if (result == NULL) { + return -1; + } + int same = result == value; + Py_DECREF(result); + return same; + } + """, + resultspec="i", + argspec="O", + arguments=["PyObject* value"], + ) + + def test_os_fspath_native_subclass(self): + for value in (FSPathUnicodeSubclass("stringpath"), FSPathBytesSubclass(b"bytespath")): + assert os.fspath(value) is value + + def test_os_fspath_native_subclass_return(self): + for value in (FSPathUnicodeSubclass("stringpath"), FSPathBytesSubclass(b"bytespath")): + assert os.fspath(FSPathWrapper(value)) is value + @unittest.skipUnless(sys.implementation.name == 'graalpy', "GraalPy-only") def test_graalpy_version(): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_object.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_object.py index ad5876e3d7..bf9a6d9d65 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_object.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_object.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 @@ -41,7 +41,7 @@ import unittest from unittest import skipIf -from . import CPyExtType, CPyExtTestCase, CPyExtFunction, unhandled_error_compare, assert_raises +from . import CPyExtType, CPyExtTestCase, CPyExtFunction, compile_module_from_string, unhandled_error_compare, assert_raises is_windows = sys.platform == "win32" @@ -91,6 +91,55 @@ def __getattribute__(self, key): class TestObject(unittest.TestCase): + def test_iter_dict_before_getattr_property(self): + module = compile_module_from_string( + """ + #include + + static PyObject* iter_dict_then_get_attr(PyObject* self, PyObject* obj) { + PyObject* dict = PyObject_GetAttrString(obj, "__dict__"); + if (dict == NULL) { + return NULL; + } + + Py_ssize_t pos = 0; + PyObject *key, *value; + while (PyDict_Next(dict, &pos, &key, &value)) { + Py_INCREF(value); + Py_DECREF(value); + } + + Py_DECREF(dict); + return PyObject_GetAttrString(obj, "area"); + } + + static PyMethodDef methods[] = { + {"iter_dict_then_get_attr", (PyCFunction)iter_dict_then_get_attr, METH_O, NULL}, + {NULL, NULL, 0, NULL} + }; + + static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, "test_iter_dict_before_getattr_property", NULL, -1, methods + }; + + PyMODINIT_FUNC PyInit_test_iter_dict_before_getattr_property(void) { + return PyModule_Create(&module); + } + """, + "test_iter_dict_before_getattr_property", + ) + + class Model: + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + @property + def area(self): + return b"%d" % (self.width * self.height) + + self.assertEqual(module.iter_dict_then_get_attr(Model(width=3, height=4)), b"12") + def test_add(self): TestAdd = CPyExtType("TestAdd", """ @@ -659,11 +708,56 @@ class MyClass: assert tester.get_tp_name(int) == 'int' assert tester.get_tp_name(type(tester)) == 'TestTpName.TestTpName' + def test_new_exception_with_qualified_name(self): + module = compile_module_from_string(""" + #include "Python.h" + + static PyModuleDef testmodule = { + PyModuleDef_HEAD_INIT, + "test_new_exception_with_qualified_name", + NULL, + -1, + NULL, + }; + + PyMODINIT_FUNC PyInit_test_new_exception_with_qualified_name(void) + { + PyObject *exception = PyErr_NewException("pkg.sub.CustomError", NULL, NULL); + if (exception == NULL) { + return NULL; + } + PyObject *module = PyModule_Create(&testmodule); + if (module == NULL) { + Py_DECREF(exception); + return NULL; + } + PyObject *name = PyObject_GetAttrString(exception, "__name__"); + if (name == NULL) { + Py_DECREF(exception); + Py_DECREF(module); + return NULL; + } + const char *name_str = PyUnicode_AsUTF8(name); + if (name_str == NULL || PyModule_AddObject(module, name_str, exception) < 0) { + Py_DECREF(name); + Py_DECREF(exception); + Py_DECREF(module); + return NULL; + } + Py_DECREF(name); + return module; + } + """, "test_new_exception_with_qualified_name") + assert module.CustomError.__name__ == "CustomError" + assert module.CustomError.__qualname__ == "CustomError" + assert module.CustomError.__module__ == "pkg.sub" + assert not hasattr(module, "pkg.sub.CustomError") + def test_tp_alloc(self): TestTpAlloc = CPyExtType("TestTpAlloc", ''' #include "objimpl.h" - + static PyObject* testslots_has_tp_alloc_and_size(PyObject* self) { if (!PyType_Type.tp_alloc) { Py_RETURN_FALSE; @@ -1457,7 +1551,6 @@ def test_take_ownership(self): obj.clear_value() assert value == (1, 2, 3, "hello", "world", dummy) - @skipIf(is_windows, reason="GR-52900") def test_async_slots(self): import asyncio, types, functools TestTpAsync = CPyExtType("TestTpAsync", @@ -1556,13 +1649,20 @@ class TestObjectFunctions(CPyExtTestCase): ), code=''' #ifdef GRAALVM_PYTHON - uintptr_t GraalPyPrivate_Long_lv_tag(const PyLongObject *op); - #define GET_LV_TAG(val) GraalPyPrivate_Long_lv_tag((PyLongObject*)val) + #include + #define GET_LV_TAG(val) GraalPyTestCAPI->LongLvTag((PyLongObject*)val) #else #define GET_LV_TAG(val) ((PyLongObject*)val)->long_value.lv_tag #endif static Py_ssize_t wrap_lv_tag(PyObject* object, PyObject* unused) { + #ifdef GRAALVM_PYTHON + if (GraalPyTestCAPI == NULL) { + if (GraalPyTestCAPI_Import() < 0) { + return -1; + } + } + #endif return GET_LV_TAG(object); } ''', @@ -1663,3 +1763,70 @@ def test_pickle_native(self): pass else: assert False, "Expected TypeError" + + def test_pickle_native_getnewargs(self): + import pickle + + TestPicklableWithNewArgs = CPyExtType( + "TestPicklableWithNewArgs", + """ + static PyObject* test_pickle_new(PyTypeObject* cls, PyObject* args, PyObject* kwargs) { + return PyType_GenericNew(cls, NULL, NULL); + } + + static PyObject* test_getnewargs(PyObject* self) { + PyObject* value = PyLong_FromLong(42); + if (value == NULL) + return NULL; + PyObject* result = PyTuple_Pack(1, value); + Py_DECREF(value); + return result; + } + """, + tp_new="test_pickle_new", + tp_methods='{"__getnewargs__", (PyCFunction)test_getnewargs, METH_NOARGS, ""}', + ) + + obj = TestPicklableWithNewArgs() + data = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL) + assert type(pickle.loads(data)) == TestPicklableWithNewArgs + + def test_pickle_native_getnewargs_ex(self): + import pickle + + TestPicklableWithNewArgsEx = CPyExtType( + "TestPicklableWithNewArgsEx", + """ + static PyObject* test_pickle_new(PyTypeObject* cls, PyObject* args, PyObject* kwargs) { + return PyType_GenericNew(cls, NULL, NULL); + } + + static PyObject* test_getnewargs_ex(PyObject* self) { + PyObject* value = PyLong_FromLong(42); + if (value == NULL) + return NULL; + PyObject* args = PyTuple_Pack(1, value); + if (args == NULL) { + Py_DECREF(value); + return NULL; + } + PyObject* kwargs = PyDict_New(); + if (kwargs == NULL) { + Py_DECREF(value); + Py_DECREF(args); + return NULL; + } + PyObject* result = PyTuple_Pack(2, args, kwargs); + Py_DECREF(value); + Py_DECREF(args); + Py_DECREF(kwargs); + return result; + } + """, + tp_new="test_pickle_new", + tp_methods='{"__getnewargs_ex__", (PyCFunction)test_getnewargs_ex, METH_NOARGS, ""}', + ) + + obj = TestPicklableWithNewArgsEx() + data = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL) + assert type(pickle.loads(data)) == TestPicklableWithNewArgsEx diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_pystate.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_pystate.py index 576291ca11..c0ec0003f8 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_pystate.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_pystate.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 @@ -72,6 +72,8 @@ def test_PyThreadState_GetFrame(self): # This seems to get the native extensions into some inconsistent state on GraalPy, giving: # refcnt below zero during managed adjustment for 0000aaae18fca780 (9 0000000000000009 - 10) def test_SetAsyncExc(self): + # TODO GR-63289 transiently failing too often + return SetAsyncExcCaller = CPyExtType( "SetAsyncExcCaller", """ diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_shutdown.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_shutdown.py index 1c7a4fca16..75ff3a6377 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_shutdown.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_shutdown.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 @@ -40,25 +40,33 @@ import os import signal import subprocess +import tempfile +import textwrap from pathlib import Path import sys +from unittest import skipIf +from tests import compile_module_from_string +from tests.util import run_subprocess_with_graalpy_startup_retry + +GRAALPY = sys.implementation.name == 'graalpy' DIR = Path(__file__).parent MODULE_PATH = DIR / 'module_with_native_destructor.py' ENV = dict(os.environ) ENV['PYTHONPATH'] = str(DIR.parent.parent) ARGS = [] -if sys.implementation.name == 'graalpy': - ARGS = ['--experimental-options', '--python.EnableDebuggingBuiltins'] +if GRAALPY: + ARGS = ['--experimental-options', f'--log.file={os.devnull}', '--python.EnableDebuggingBuiltins'] if not __graalpython__.is_native: + ARGS += [f'--vm.Djdk.graal.LogFile={os.devnull}'] ARGS += [f'--vm.Dpython.EnableBytecodeDSLInterpreter={str(__graalpython__.is_bytecode_dsl_interpreter).lower()}'] COMMAND = [sys.executable, *ARGS, str(MODULE_PATH)] # Test that running Py_DECREF in native global destructor doesn't crash def test_normal_exit(): - subprocess.run(COMMAND, check=True, env=ENV) + run_subprocess_with_graalpy_startup_retry(COMMAND, check=True, env=ENV) def test_sigterm(): @@ -67,3 +75,144 @@ def test_sigterm(): assert proc.stdout.read(len(expected)) == expected proc.terminate() assert proc.wait() in [-signal.SIGTERM, 128 + signal.SIGTERM] + + +@skipIf(not GRAALPY, "GraalPy-only native weakref shutdown test") +def test_native_weakref_shutdown_skips_c_retained_object(): + module = compile_module_from_string(textwrap.dedent(""" + #include "Python.h" + #include "structmember.h" + #include + #include + #include + + typedef struct { + PyObject_HEAD + PyObject *weakreflist; + int id; + } NativeWeakRefObject; + + static PyObject *kept_alive; + static PyTypeObject NativeWeakRefType; + + static void write_marker(const char *contents) { + const char *marker_path = getenv("GR50212_DEALLOC_MARKER"); + if (marker_path != NULL) { + FILE *marker = fopen(marker_path, "a"); + if (marker != NULL) { + fputs(contents, marker); + fclose(marker); + } + } + } + + static PyObject *new_native_weakref(int id) { + NativeWeakRefObject *obj = (NativeWeakRefObject *)PyObject_CallNoArgs((PyObject *)&NativeWeakRefType); + if (obj == NULL) { + return NULL; + } + obj->id = id; + return (PyObject *)obj; + } + + static void NativeWeakRef_dealloc(NativeWeakRefObject *self) { + write_marker(self->id == 1 ? "deallocated:held\\n" : "deallocated:free\\n"); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs((PyObject *)self); + } + Py_TYPE(self)->tp_free((PyObject *)self); + } + + static PyTypeObject NativeWeakRefType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "native_weakref_shutdown_reproducer_gr50212.NativeWeakRef", + .tp_basicsize = sizeof(NativeWeakRefObject), + .tp_dealloc = (destructor)NativeWeakRef_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_new = PyType_GenericNew, + .tp_weaklistoffset = offsetof(NativeWeakRefObject, weakreflist), + }; + + static PyObject *hold(PyObject *self, PyObject *Py_UNUSED(ignored)) { + Py_CLEAR(kept_alive); + kept_alive = new_native_weakref(1); + if (kept_alive == NULL) { + return NULL; + } + write_marker("created:held\\n"); + return Py_NewRef(kept_alive); + } + + static PyObject *make(PyObject *self, PyObject *Py_UNUSED(ignored)) { + PyObject *obj = new_native_weakref(2); + if (obj == NULL) { + return NULL; + } + write_marker("created:free\\n"); + return obj; + } + + static PyMethodDef methods[] = { + {"hold", hold, METH_NOARGS, ""}, + {"make", make, METH_NOARGS, ""}, + {NULL, NULL, 0, NULL}, + }; + + static PyModuleDef module = { + PyModuleDef_HEAD_INIT, + "native_weakref_shutdown_reproducer_gr50212", + "", + -1, + methods, + NULL, NULL, NULL, NULL + }; + + PyMODINIT_FUNC PyInit_native_weakref_shutdown_reproducer_gr50212(void) { + if (PyType_Ready(&NativeWeakRefType) < 0) { + return NULL; + } + PyObject *m = PyModule_Create(&module); + if (m == NULL) { + return NULL; + } + Py_INCREF(&NativeWeakRefType); + if (PyModule_AddObject(m, "NativeWeakRef", (PyObject *)&NativeWeakRefType) < 0) { + Py_DECREF(&NativeWeakRefType); + Py_DECREF(m); + return NULL; + } + return m; + } + """), "native_weakref_shutdown_reproducer_gr50212") + module_dir = str(Path(module.__file__).parent) + args = [ + sys.executable, + '--experimental-options', + '--python.EnableDebuggingBuiltins', + ] + if not __graalpython__.is_native: + args += [f'--vm.Dpython.EnableBytecodeDSLInterpreter={str(__graalpython__.is_bytecode_dsl_interpreter).lower()}'] + code = textwrap.dedent(""" + import gc + import weakref + import native_weakref_shutdown_reproducer_gr50212 + + held = native_weakref_shutdown_reproducer_gr50212.hold() + free = native_weakref_shutdown_reproducer_gr50212.make() + held_wr = weakref.ref(held) + free_wr = weakref.ref(free) + type_wr = weakref.ref(native_weakref_shutdown_reproducer_gr50212.NativeWeakRef) + print(type(held).__name__, type(free).__name__, flush=True) + del free + for _ in range(3): + gc.collect() + print(held_wr() is not None, free_wr() is None, flush=True) + """) + env = dict(ENV) + env["PYTHONPATH"] = os.pathsep.join([module_dir, env["PYTHONPATH"]]) + with tempfile.TemporaryDirectory() as tmpdir: + marker = Path(tmpdir) / "deallocated" + env["GR50212_DEALLOC_MARKER"] = str(marker) + proc = subprocess.run([*args, '-c', code], env=env, capture_output=True, text=True, check=True) + assert proc.stdout.splitlines() == ["NativeWeakRef NativeWeakRef", "True True"] + assert marker.read_text().splitlines() == ["created:held", "created:free", "deallocated:free"] diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_sys.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_sys.py index ea5c0d2b95..cebff02cf5 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_sys.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_sys.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 @@ -39,7 +39,12 @@ import sys -from . import CPyExtTestCase, CPyExtFunction, unhandled_error_compare +from . import ( + CPyExtFunction, + CPyExtTestCase, + compile_module_from_string, + unhandled_error_compare, +) def _reference_get_object(args): @@ -49,7 +54,76 @@ def _reference_get_object(args): raise SystemError # raised by PyBuildValue(..., NULL) class TestPySys(CPyExtTestCase): - + def test_PySys_Audit(self): + module = compile_module_from_string(""" + #define PY_SSIZE_T_CLEAN + #include + + static PyObject* audit(PyObject* self, PyObject* Py_UNUSED(args)) { + PyObject *value = PyUnicode_FromString("value"); + if (value == NULL) { + return NULL; + } + int res = PySys_Audit("graalpy.test_capi_audit", "Oi", value, 23); + Py_DECREF(value); + if (res < 0) { + return NULL; + } + return PyLong_FromLong(res); + } + + static PyObject* audit_error(PyObject* self, PyObject* Py_UNUSED(args)) { + PyObject *value = PyUnicode_FromString("value"); + if (value == NULL) { + return NULL; + } + int res = PySys_Audit("graalpy.test_capi_audit_error", "O", value); + Py_DECREF(value); + if (res < 0) { + return NULL; + } + return PyLong_FromLong(res); + } + + static PyMethodDef methods[] = { + {"audit", audit, METH_NOARGS, NULL}, + {"audit_error", audit_error, METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} + }; + + static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, "test_sys_audit", NULL, -1, methods + }; + + PyMODINIT_FUNC PyInit_test_sys_audit(void) { + return PyModule_Create(&module); + } + """, "test_sys_audit") + + seen = [] + + def hook(event, args): + if event == "graalpy.test_capi_audit": + seen.append(args) + + sys.addaudithook(hook) + + self.assertEqual(module.audit(), 0) + self.assertEqual(seen, [("value", 23)]) + + class AuditError(Exception): + pass + + def error_hook(event, args): + if event == "graalpy.test_capi_audit_error": + raise AuditError(args) + + sys.addaudithook(error_hook) + + with self.assertRaises(AuditError) as cm: + module.audit_error() + self.assertEqual(cm.exception.args[0], ("value",)) + test_PySys_GetObject = CPyExtFunction( _reference_get_object, lambda: ( 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..fe01afd7c2 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 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..7e7902b682 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 @@ -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 @@ -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', @@ -149,7 +147,7 @@ class TestPyTuple(CPyExtTestCase): arguments=["PyObject* tuple", "Py_ssize_t index"], resulttype="PyObject*", ) - + test_PyTuple_SetItem = CPyExtFunction( _reference_setitem, lambda: ( @@ -296,6 +294,36 @@ class TestPyTuple(CPyExtTestCase): ) +class TestNativeTupleStorage(unittest.TestCase): + def test_marshal_native_tuple(self): + import marshal + + TestNativeTupleFactory = CPyExtType( + "TestNativeTupleFactory", + """ + static PyObject* get_tuple(PyObject* cls) { + PyObject* value = PyLong_FromLong(42); + if (value == NULL) + return NULL; + PyObject* text = PyUnicode_FromString("native"); + if (text == NULL) { + Py_DECREF(value); + return NULL; + } + PyObject* result = PyTuple_Pack(2, value, text); + Py_DECREF(value); + Py_DECREF(text); + return result; + } + """, + tp_methods='{"get_tuple", (PyCFunction)get_tuple, METH_NOARGS | METH_CLASS, ""}', + ) + + native_tuple = TestNativeTupleFactory.get_tuple() + assert is_native_object(native_tuple) + assert marshal.loads(marshal.dumps(native_tuple)) == (42, "native") + + class TestNativeSubclass(unittest.TestCase): def _verify(self, t): assert is_native_object(t) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py index a990dff177..d3f634b272 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_unicode.py @@ -79,6 +79,13 @@ def _reference_unicode_escape(args): return _codecs.unicode_escape_encode(args[0])[0] +def _reference_unicode_new(args): + expected = args[3] + if isinstance(expected, type) and issubclass(expected, BaseException): + raise expected() + return expected + + def _reference_fromformat(args): fmt = args[0] fmt_args = args[1:] @@ -897,7 +904,7 @@ class TestPyUnicode(CPyExtTestCase): ) test_PyUnicode_New = CPyExtFunction( - lambda args: args[3], + _reference_unicode_new, lambda: ( (134818, bytearray([0xA2, 0x0E, 0x02, 0x00]), 1, "𠺢"), (134988, bytearray([0xA2, 0x0E, 0x02, 0x00, 0x4C, 0x0F, 0x02, 0x00]), 2, "𠺢𠽌"), @@ -905,9 +912,15 @@ class TestPyUnicode(CPyExtTestCase): (8252, bytearray([0x30, 0x20, 0x3C, 0x20]), 2, "‰‼"), (127, bytearray([0x61, 0x62, 0x63, 0x64]), 4, "abcd"), (127, bytearray([0x61, 0x62, 0x63, 0x64]), 2, "ab"), + (127, bytearray(), -1, SystemError), + (0x10ffff, bytearray(), sys.maxsize, MemoryError), + (0x110000, bytearray(), 1, SystemError), ), code='''PyObject* wrap_PyUnicode_New(Py_ssize_t maxchar, Py_buffer buffer, Py_ssize_t nchars, PyObject* dummy) { PyObject* obj = PyUnicode_New(nchars, (Py_UCS4) maxchar); + if (obj == NULL) { + return NULL; + } void* data = PyUnicode_DATA(obj); size_t char_size; if (maxchar < 256) { @@ -1056,9 +1069,10 @@ class TestPyUnicode(CPyExtTestCase): ) test_PyUnicode_FSDecoder = CPyExtFunction( - lambda args: str(args[0]), + lambda args: os.fsdecode(args[0]), lambda: ( (os.path.realpath(os_helper.TESTFN),), + (b"name-\xff",), ), code='''PyObject* wrap_PyUnicode_FSDecoder(PyObject* path) { PyObject* res; @@ -1076,6 +1090,51 @@ class TestPyUnicode(CPyExtTestCase): cmpfunc=unhandled_error_compare ) + test_PyUnicode_DecodeFSDefault = CPyExtFunction( + lambda args: os.fsdecode(args[0]), + lambda: ( + (b"name",), + (b"name-\xff",), + ), + code='''PyObject* wrap_PyUnicode_DecodeFSDefault(PyObject* path) { + char* data; + Py_ssize_t size; + if (PyBytes_AsStringAndSize(path, &data, &size) < 0) { + return NULL; + } + return PyUnicode_DecodeFSDefault(data); + } + ''', + resultspec="O", + argspec='O', + arguments=["PyObject* path"], + callfunction="wrap_PyUnicode_DecodeFSDefault", + cmpfunc=unhandled_error_compare + ) + + test_PyUnicode_DecodeFSDefaultAndSize = CPyExtFunction( + lambda args: os.fsdecode(args[0][:args[1]]), + lambda: ( + (b"name", 4), + (b"name-\xff", 6), + (b"name-\xff-suffix", 6), + ), + code='''PyObject* wrap_PyUnicode_DecodeFSDefaultAndSize(PyObject* path, Py_ssize_t size) { + char* data; + Py_ssize_t path_size; + if (PyBytes_AsStringAndSize(path, &data, &path_size) < 0) { + return NULL; + } + return PyUnicode_DecodeFSDefaultAndSize(data, size); + } + ''', + resultspec="O", + argspec='On', + arguments=["PyObject* path", "Py_ssize_t size"], + callfunction="wrap_PyUnicode_DecodeFSDefaultAndSize", + cmpfunc=unhandled_error_compare + ) + class TestUnicodeObject(unittest.TestCase): def test_intern(self): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/ssldata/cert_dsa.pem b/graalpython/com.oracle.graal.python.test/src/tests/ssldata/cert_dsa.pem new file mode 100644 index 0000000000..c9a8c010d7 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/ssldata/cert_dsa.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEYzCCBBCgAwIBAgIUa4FezfhUiUvkPAwJQ2GNI7HJNCkwCwYJYIZIAWUDBAMC +MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yNjAzMTYxNjQ4NTBaFw0zNjAzMTMx +NjQ4NTBaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCA0MwggI1BgcqhkjOOAQBMIIC +KAKCAQEAt93WIFjdg9j0JO4MIZzgnzgEQa1CJky7vLfS0S1YetqzE1tyXeWNx/Kj +sxm9OdL8Xvnb5LJKoLJbFZ0yV0/tOqnA1pNn/QH3Fh3p2l9yADu9V6GYcxmwo1U1 +PlZ+oHoj5IFIoQZeENSlPPrTWTj/y0WjGJNwzRlIVTNJpP3vUw5Shf6RmGPq1uJs +RPJRSPiBZ9fccXyffrOpOizrS4HfQpmAsva4/Ijlc1Hn7Ko1uRLMGARpeGmpzxis +tkD4Fns5YH5k21cL3+ZkNlCJAqRZgGwyL+M0WB/LEnk/mlhgt9s0WHLpoxoEPllc +UvPsJ+ZUKPnieijiP0wOMEIF9SvdwwIdANAJ0cXh8PGBvIfV6Wg+3p8Jz64Cz3g7 +McVpzuMCggEAaEER2Jrd9lmlQdDjkH21GaMkiwpNnSExwMwnAjprZe+n+3/9Xv6e +dNJKxK9ITx7xe+CCjfOl3M0m/Spo+hLoL9dLiJ/3ZfxfRkfDizWTxwbh6jDzEfh5 +vW0Xkx1aALRudGH8t6VnlBrSCjj0r6Ih7z3m0eTe/3PV47p9CrD0KLUiKS+GcDYT +rgglG/jLhKEaR22/T2jLP4VaE15Yi6Vc9LcFwQyLrKytDgqsChqaJzAl/72YWzob +tFkCUM94h0Jx7vMTlIwhBihxya4yAiERxZFKENhs1mhMLMu2fCk2K3BChMVm42fE +UFywxOvChj2DOQvYQTOuTNz/yyqONZEuxQOCAQYAAoIBAQCHmjdKyPjTSl5ErgvA +gG4+h1Z5NBty1dgORCB8JK8IkUojAre8nvZvExFSFBjyr5TCvLR6vDvB5sn16+zP +w7QNMDPvLNZ74lBjcnuNXkc9XAuJlj53zSLQy/jvi+Hf1UknAfnfrIXQ/v40Pmz5 +hyhRbLjOiF6azm56DzsFRdoi88cihRnI0pSDaaE09/sPJdZVEmWfLKEbFAQGSo55 +f5aoSOpFzUElFp/OdHAj8InuhxP8QXPFYx5xmUAvc1yyw47Qz9Fp6ewFZoE3LquC +GwuXfhMxFGVbD+f2hQhRHDHfOSMg+W6y3+7ZG5mYq+aRwM10/FA2TRL5efzaBuV7 +baTUo1MwUTAdBgNVHQ4EFgQUIbSBGsQPPVVo0LrkNC1f5viGw0owHwYDVR0jBBgw +FoAUIbSBGsQPPVVo0LrkNC1f5viGw0owDwYDVR0TAQH/BAUwAwEB/zALBglghkgB +ZQMEAwIDQAAwPQIcccMSDglufq9UQP3wfXaqW4GOtP5/Ph3R9q9X2gIdAJRsMoM9 +X6l+d5A6kXV+uaXweaZFnoZ9Mo6+JAI= +-----END CERTIFICATE----- diff --git a/graalpython/com.oracle.graal.python.test/src/tests/ssldata/pk_dsa_legacy.pem b/graalpython/com.oracle.graal.python.test/src/tests/ssldata/pk_dsa_legacy.pem new file mode 100644 index 0000000000..381ccf8c60 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/ssldata/pk_dsa_legacy.pem @@ -0,0 +1,20 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIDTgIBAAKCAQEAt93WIFjdg9j0JO4MIZzgnzgEQa1CJky7vLfS0S1YetqzE1ty +XeWNx/Kjsxm9OdL8Xvnb5LJKoLJbFZ0yV0/tOqnA1pNn/QH3Fh3p2l9yADu9V6GY +cxmwo1U1PlZ+oHoj5IFIoQZeENSlPPrTWTj/y0WjGJNwzRlIVTNJpP3vUw5Shf6R +mGPq1uJsRPJRSPiBZ9fccXyffrOpOizrS4HfQpmAsva4/Ijlc1Hn7Ko1uRLMGARp +eGmpzxistkD4Fns5YH5k21cL3+ZkNlCJAqRZgGwyL+M0WB/LEnk/mlhgt9s0WHLp +oxoEPllcUvPsJ+ZUKPnieijiP0wOMEIF9SvdwwIdANAJ0cXh8PGBvIfV6Wg+3p8J +z64Cz3g7McVpzuMCggEAaEER2Jrd9lmlQdDjkH21GaMkiwpNnSExwMwnAjprZe+n ++3/9Xv6edNJKxK9ITx7xe+CCjfOl3M0m/Spo+hLoL9dLiJ/3ZfxfRkfDizWTxwbh +6jDzEfh5vW0Xkx1aALRudGH8t6VnlBrSCjj0r6Ih7z3m0eTe/3PV47p9CrD0KLUi +KS+GcDYTrgglG/jLhKEaR22/T2jLP4VaE15Yi6Vc9LcFwQyLrKytDgqsChqaJzAl +/72YWzobtFkCUM94h0Jx7vMTlIwhBihxya4yAiERxZFKENhs1mhMLMu2fCk2K3BC +hMVm42fEUFywxOvChj2DOQvYQTOuTNz/yyqONZEuxQKCAQEAh5o3Ssj400peRK4L +wIBuPodWeTQbctXYDkQgfCSvCJFKIwK3vJ72bxMRUhQY8q+Uwry0erw7webJ9evs +z8O0DTAz7yzWe+JQY3J7jV5HPVwLiZY+d80i0Mv474vh39VJJwH536yF0P7+ND5s ++YcoUWy4zohems5ueg87BUXaIvPHIoUZyNKUg2mhNPf7DyXWVRJlnyyhGxQEBkqO +eX+WqEjqRc1BJRafznRwI/CJ7ocT/EFzxWMecZlAL3NcssOO0M/RaensBWaBNy6r +ghsLl34TMRRlWw/n9oUIURwx3zkjIPlust/u2RuZmKvmkcDNdPxQNk0S+Xn82gbl +e22k1AIcANOj4eSn45Oi3o7NSpqU/LZ0PYo5tD1osrjZTA== +-----END DSA PRIVATE KEY----- diff --git a/graalpython/com.oracle.graal.python.test/src/tests/ssldata/pk_ecc_legacy.pem b/graalpython/com.oracle.graal.python.test/src/tests/ssldata/pk_ecc_legacy.pem new file mode 100644 index 0000000000..580e5ef888 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/ssldata/pk_ecc_legacy.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBL2Y5JfpzbgHw+t4Q+c5SHhsZcD9ylEtUMg7OyF9xW6j+3VIVORGao +kcOtE0Z2Y5egBwYFK4EEACKhZANiAASzz/rInKUzonpxP5bLxmq8fmrtgRSS0jRP +UOU16XKX+KtifnLbmLHQtPrctdkRRROCxnURz2fBihQTJkXyBMSswNTRCs+4DUKb +MAfihigMVYgdWbZPFBDleo5aeFw4/FM= +-----END EC PRIVATE KEY----- diff --git a/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_standalone.py b/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_standalone.py index 6b51f714dd..1ed2b27f52 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_standalone.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/standalone/test_standalone.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 @@ -38,6 +38,7 @@ # SOFTWARE. import os +import sys import tempfile import unittest @@ -69,6 +70,9 @@ def create_test_env(): env["PIP_CONSTRAINT"] = constraints_file return env +def append_env_flag(env, key, flag): + env[key] = (env.get(key, "") + " " + flag).strip() + @unittest.skipUnless(is_enabled, "ENABLE_STANDALONE_UNITTESTS is not true") def test_native_executable_one_file(): graalpy = util.get_gp() @@ -124,7 +128,10 @@ def test_native_executable_venv_and_one_file(): venv_python = os.path.join(venv_dir, "Scripts", "python.exe") if os.name == "nt" else os.path.join(venv_dir, "bin", "python") cmd = [venv_python, "-m", "pip", "install", "termcolor", "ujson"] - _, return_code = util.run_cmd(cmd, env, logger=log) + pip_env = env.copy() + if sys.platform == "darwin": + append_env_flag(pip_env, "CXXFLAGS", "-std=c++11") + _, return_code = util.run_cmd(cmd, pip_env, logger=log) assert return_code == 0, log target_file = os.path.join(target_dir, "hello") 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 f0cb704814..c7ff052f1a 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 @@ -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 @@ -37,9 +37,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import copy +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): @@ -101,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) @@ -112,6 +115,53 @@ def test_array_native_storage(): a.insert(1, -1) assert a == array('l', [1, -1, 3]) + +def test_copy(): + a = array('l', [1, 2, 3]) + b = a.__copy__() + c = copy.copy(a) + assert type(b) is array + assert b == a + assert c == a + assert b is not a + assert c is not a + a[0] = 42 + assert b == array('l', [1, 2, 3]) + assert c == array('l', [1, 2, 3]) + + +@skip_if_sandboxed("Needs native storage support in sandboxed runs") +def test_copy_native_storage(): + a = array('l', [1, 2, 3]) + storage_to_native(a) + b = a.__copy__() + assert b == a + a[1] = 42 + assert b == array('l', [1, 2, 3]) + + +def test_deepcopy(): + a = array('l', [1, 2, 3]) + b = a.__deepcopy__({}) + c = copy.deepcopy(a) + assert type(b) is array + assert b == a + assert c == a + assert b is not a + assert c is not a + a[0] = 42 + assert b == array('l', [1, 2, 3]) + assert c == array('l', [1, 2, 3]) + + +def test_class_getitem(): + alias = array[int] + assert isinstance(alias, types.GenericAlias) + assert alias.__origin__ is array + assert alias.__args__ == (int,) + assert array.__class_getitem__(str).__args__ == (str,) + + def test_mul(): a = array('l', [1, 2, 3]) assert len(a * 0) == 0 @@ -119,4 +169,4 @@ def test_mul(): b = a * 1 a[2] = 42 assert list(a) == [1, 2, 42] - assert list(b) == [1, 2, 3] \ No newline at end of file + assert list(b) == [1, 2, 3] diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_ast.py b/graalpython/com.oracle.graal.python.test/src/tests/test_ast.py index c4f30e6914..71c7fc2dbb 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_ast.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_ast.py @@ -1,4 +1,4 @@ -# 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 @@ -193,5 +193,20 @@ def test_parse_empty_module(self): assert eval(code) is None assert isinstance(code, types.CodeType) + def test_compile_match_singleton_none(self): + # This occurs in pytest-generated ASTs + m = ast.Module( + body=[ + ast.Match( + subject=ast.Name("v", ast.Load()), + cases=[ast.match_case(pattern=ast.MatchSingleton(None), body=[ast.Pass()])], + ) + ], + type_ignores=[], + ) + m = ast.fix_missing_locations(m) + code = compile(m, "", "exec") + assert isinstance(code, types.CodeType) + if __name__ == '__main__': unittest.main() diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_audit_hooks.py b/graalpython/com.oracle.graal.python.test/src/tests/test_audit_hooks.py new file mode 100644 index 0000000000..e941dc779c --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_audit_hooks.py @@ -0,0 +1,129 @@ +# 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 sys +import tempfile +import unittest + + +class AuditHookTests(unittest.TestCase): + def test_sys_audit_calls_registered_hook(self): + seen = [] + + def hook(event, args): + if event == "graalpy.test_sys_audit": + seen.append((event, args)) + + sys.addaudithook(hook) + sys.audit("graalpy.test_sys_audit", 1, "two") + + self.assertEqual(seen, [("graalpy.test_sys_audit", (1, "two"))]) + + def test_sys_audit_propagates_hook_exception(self): + class AuditError(Exception): + pass + + def hook(event, args): + if event == "graalpy.test_sys_audit_error": + raise AuditError(args) + + sys.addaudithook(hook) + + with self.assertRaises(AuditError): + sys.audit("graalpy.test_sys_audit_error", 42) + + def test_java_audit_site_calls_registered_hook(self): + seen = [] + + def hook(event, args): + if event == "open": + seen.append(args) + + sys.addaudithook(hook) + with tempfile.TemporaryFile("w"): + pass + + self.assertTrue(seen) + + def test_addaudithook_exception_blocks_new_hook(self): + seen = [] + block_add = True + + def blocking_hook(event, args): + nonlocal block_add + if event == "sys.addaudithook": + if not block_add: + return + block_add = False + raise RuntimeError("blocked") + if event == "graalpy.test_blocked_hook": + seen.append("blocking") + + def blocked_hook(event, args): + if event == "graalpy.test_blocked_hook": + seen.append("blocked") + + sys.addaudithook(blocking_hook) + sys.addaudithook(blocked_hook) + sys.audit("graalpy.test_blocked_hook") + + self.assertEqual(seen, ["blocking"]) + + def test_addaudithook_propagates_baseexception(self): + class AuditBaseException(BaseException): + pass + + block_add = True + + def hook(event, args): + nonlocal block_add + if event == "sys.addaudithook": + if not block_add: + return + block_add = False + raise AuditBaseException + + sys.addaudithook(hook) + + with self.assertRaises(AuditBaseException): + sys.addaudithook(lambda event, args: None) + + +if __name__ == "__main__": + unittest.main() diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_builtin.py b/graalpython/com.oracle.graal.python.test/src/tests/test_builtin.py index 594270bde7..ad7bc47f6c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_builtin.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_builtin.py @@ -1,8 +1,11 @@ -# Copyright (c) 2018, 2023, 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 - +import os +import subprocess +import sys +import tempfile import unittest class MyIndexable(object): @@ -38,6 +41,24 @@ def test_divmod_complex(self): self.assertRaises(TypeError, divmod, 10, c2) self.assertRaises(TypeError, divmod, c1, 10) + def test_aiter_type_errors(self): + class BadAIter: + def __aiter__(self): + return object() + + async def async_for_none(): + async for _ in None: + pass + + with self.assertRaisesRegex(TypeError, "'NoneType' object is not an async iterable"): + aiter(None) + with self.assertRaisesRegex(TypeError, "'int' object is not an async iterable"): + aiter(1) + with self.assertRaisesRegex(TypeError, r"aiter\(\) returned not an async iterator of type 'object'"): + aiter(BadAIter()) + with self.assertRaisesRegex(TypeError, "'async for' requires an object with __aiter__ method, got NoneType"): + async_for_none().send(None) + def test_getitem_typeerror(self): a = object() try: @@ -51,6 +72,18 @@ def test_ascii(self): self.assertEqual(ascii(1), "1") self.assertEqual(ascii("錦蛇 \t \0 a \x03"), "'\\u9326\\u86c7 \\t \\x00 a \\x03'") + def test_ascii_preserves_str_subclass_for_ascii_repr(self): + class MyStr(str): + pass + + class Foo: + def __repr__(self): + return MyStr("hello") + + result = ascii(Foo()) + self.assertEqual(result, "hello") + self.assertIs(type(result), MyStr) + def test_input(self): import sys class AlwaysLine: @@ -68,7 +101,7 @@ def test_chr(self): self.assertEqual(chr(97), 'a') self.assertEqual(chr(0xfff), '\u0fff') self.assertEqual(chr(0xf0000), '\U000f0000') - + def test_ord(self): self.assertEqual(ord(' '), 32) self.assertEqual(ord('a'), 97) @@ -93,19 +126,26 @@ def test_min(self): def test_sort_keyfunc(self): lists = [[], [1], [1,2], [1,2,3], [1,3,2], [3,2,1], [9,3,8,1,7,9,3,6,7,8]] - + for l in lists: count = 0 - + def keyfunc(v): nonlocal count count += 1 return v - + result = sorted(l, key = keyfunc) self.assertEqual(len(l), count) self.assertEqual(sorted(l), result) count = 0 result = sorted(l, key = keyfunc, reverse = True) self.assertEqual(len(l), count) - self.assertEqual(sorted(l, reverse = True), result) + self.assertEqual(sorted(l, reverse = True), result) + + def test_license(self): + with tempfile.TemporaryDirectory() as tmpdir: + # Test that it can find the license even when ran outside of the distribution + license = subprocess.check_output([sys.executable, "-c", "print(license())"], cwd=tmpdir, text=True, input=("\n" * 100)) + if sys.implementation.name == 'graalpy': + self.assertIn('Oracle', license) 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_bz2.py b/graalpython/com.oracle.graal.python.test/src/tests/test_bz2.py new file mode 100644 index 0000000000..8ba9a30631 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_bz2.py @@ -0,0 +1,59 @@ +# 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. +import bz2 +import unittest + +from tests.util import is_native_compression_backend + +DATA = (b"native bz2 support migration test data\n" * 128) + bytes(range(256)) + + +@unittest.skipUnless(is_native_compression_backend(), "requires native compression backend") +def test_bz2_stream_roundtrip_and_unused_data(): + compressor = bz2.BZ2Compressor(5) + compressed = compressor.compress(DATA[:97]) + compressed += compressor.compress(DATA[97:]) + compressed += compressor.flush() + + assert bz2.decompress(compressed) == DATA + + decompressor = bz2.BZ2Decompressor() + assert decompressor.decompress(compressed + b"tail") == DATA + assert decompressor.eof + assert decompressor.unused_data == b"tail" diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_call-classmethod.py b/graalpython/com.oracle.graal.python.test/src/tests/test_call-classmethod.py index 043a01e544..633b298375 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_call-classmethod.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_call-classmethod.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2021, Oracle and/or its affiliates. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. # Copyright (c) 2013, Regents of the University of California # # All rights reserved. @@ -23,6 +23,9 @@ # OF THE POSSIBILITY OF SUCH DAMAGE. # classmethod calls +from tests.util import needs_capi + + class Strength(object): def __init__(self, strength, name): @@ -47,3 +50,138 @@ def test_strength(): assert not Strength.weaker(s1, s2) assert s1.stronger(s1, s2) assert not s1.weaker(s1, s2) + + +def test_classmethod_wraps_descriptor(): + class Descriptor: + def __get__(self, obj, typ=None): + return obj, typ + + class C: + method = classmethod(Descriptor()) + + assert C.method == (C, C) + assert C().method == (C, C) + + method = C.__dict__["method"] + assert method.__get__(None, C) == (C, C) + assert method.__get__(C()) == (C, C) + + +def test_classmethod_wraps_property(): + class C: + @classmethod + @property + def name(cls): + return cls.__name__ + + class D(C): + pass + + assert C.name == "C" + assert C().name == "C" + assert D.name == "D" + assert D().name == "D" + + +def test_classmethod_wraps_staticmethod(): + class C: + method = classmethod(staticmethod(lambda value: ("static", value))) + + assert C.method("arg") == ("static", "arg") + assert C().method("arg") == ("static", "arg") + + +def test_classmethod_wraps_classmethod(): + class C: + def method(cls, value): + return cls, value + + method = classmethod(classmethod(method)) + + class D(C): + pass + + assert C.method("arg") == (C, "arg") + assert C().method("arg") == (C, "arg") + assert D.method("arg") == (D, "arg") + assert D().method("arg") == (D, "arg") + + +def test_classmethod_wraps_bound_method(): + class C: + def method(self, cls): + return self, cls + + receiver = C() + assert not hasattr(type(receiver.method), "__get__") + + class D: + method = classmethod(receiver.method) + + assert D.method() == (receiver, D) + assert D().method() == (receiver, D) + + +def test_classmethod_descriptor_get_errors(): + descriptor = dict.__dict__["fromkeys"] + + assert descriptor.__get__(None, dict)([1, 2]) == {1: None, 2: None} + assert descriptor.__get__({})([1, 2]) == {1: None, 2: None} + + for args in ((None, None), (42,), (None, 42), (None, int), ({}, int)): + try: + descriptor.__get__(*args) + except TypeError: + pass + else: + raise AssertionError("classmethod_descriptor.__get__ accepted invalid arguments") + + +def test_classmethod_descriptor_get_uses_object_type_when_type_omitted(): + class MyDict(dict): + pass + + descriptor = dict.__dict__["fromkeys"] + bound = descriptor.__get__(MyDict()) + assert bound.__self__ is MyDict + assert type(bound([1, 2])) is MyDict + + +def test_classmethod_descriptor_get_does_not_keep_type_alive(): + import time + from test import support + + descriptor = object.__dict__["__init_subclass__"] + + class Parent: + pass + + class Child(Parent): + pass + + bound = descriptor.__get__(None, Child) + del bound + assert Parent.__subclasses__() == [Child] + + del Child + for _ in range(100): + support.gc_collect() + if not Parent.__subclasses__(): + break + if getattr(support, "is_graalpy", False): + time.sleep(0.1) + assert Parent.__subclasses__() == [] + + +@needs_capi +def test_cext_classmethod_descriptor(): + from _ctypes import _SimpleCData + + class c_void_p(_SimpleCData): + _type_ = "P" + + descriptor = c_void_p.__dict__["from_param"] + assert type(descriptor).__name__ == "classmethod_descriptor" + assert callable(descriptor.__get__(None, c_void_p)) + c_void_p.from_param(0) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_class.py b/graalpython/com.oracle.graal.python.test/src/tests/test_class.py index bfd53b0398..e950478bfe 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_class.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_class.py @@ -1,4 +1,4 @@ -# 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 @@ -117,10 +117,54 @@ class notAMeta(metaclass=Meta): pass notAMeta2 = type("notAMeta2", (notAMeta,), {}) - # the below assertions should pass, but this is such an unusual case that we - # ignore this. - # assert notAMeta.metatype is NewDescriptor - # assert notAMeta2.metatype is NewDescriptor + assert notAMeta.metatype is NewDescriptor + assert notAMeta2.metatype is NewDescriptor + + +def test_meta_new_default_metatype(): + class Meta(type): + def __new__(*args, **kwargs): + cls = type.__new__(*args, **kwargs) + cls.metatype = "meta" + return cls + + class uses_meta(metaclass=Meta): + pass + + uses_meta2 = type("uses_meta2", (uses_meta,), {}) + + assert uses_meta.metatype == "meta" + assert uses_meta2.metatype == "meta" + + +def test_meta_meta_new_custom_getattribute(): + meta_new_calls = [] + + class MetaMeta(type): + def __getattribute__(self, name): + if name == "__new__": + def new(*args, **kwargs): + cls = type.__new__(*args, **kwargs) + cls.metatype = "getattribute" + return cls + return new + return super().__getattribute__(name) + + class Meta(type, metaclass=MetaMeta): + def __new__(*args, **kwargs): + meta_new_calls.append(True) + cls = type.__new__(*args, **kwargs) + cls.metatype = "meta" + return cls + + class uses_getattribute(metaclass=Meta): + pass + + uses_getattribute2 = type("uses_getattribute2", (uses_getattribute,), {}) + + assert uses_getattribute.metatype == "getattribute" + assert uses_getattribute2.metatype == "getattribute" + assert meta_new_calls == [] def test_subclasses_collection(): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_cmd_line.py b/graalpython/com.oracle.graal.python.test/src/tests/test_cmd_line.py new file mode 100644 index 0000000000..642c1153cf --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_cmd_line.py @@ -0,0 +1,73 @@ +# 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 subprocess +import sys +import unittest + +IS_GRAALPY = sys.implementation.name == "graalpy" + + +class CmdLineTest(unittest.TestCase): + + def test_stdin_script_exit_code(self): + code = "import sys\nsys.exit(42)\n" + result = subprocess.run([sys.executable], input=code, text=True) + self.assertEqual(42, result.returncode) + + @unittest.skipUnless(IS_GRAALPY, "GraalPy-specific test") + def test_jit_mode_presets(self): + for mode in ('0', '1', '2'): + result = subprocess.run( + [sys.executable, '-X', f'jit={mode}', '-c', '1'], + capture_output=True, + text=True, + ) + self.assertEqual(0, result.returncode, result) + + @unittest.skipUnless(IS_GRAALPY, "GraalPy-specific test") + def test_jit_mode_invalid_value(self): + result = subprocess.run( + [sys.executable, '-X', 'jit=3', '-c', 'pass'], + capture_output=True, + text=True, + ) + self.assertNotEqual(0, result.returncode) + self.assertIn('expected jit=0, jit=1, or jit=2', result.stderr) + diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_code.py b/graalpython/com.oracle.graal.python.test/src/tests/test_code.py index b34bc155a5..6e78575bfc 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_code.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_code.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 @@ -180,17 +180,11 @@ def inner(): code = compile(codestr, "", "exec") assert "module doc" in code.co_consts - assert 1 in code.co_consts assert "fn doc" not in code.co_consts for const in code.co_consts: if type(const) == types.CodeType: code = const assert "fn doc" in code.co_consts - assert "this is fun" not in code.co_consts - for const in code.co_consts: - if type(const) == types.CodeType: - code = const - assert "this is fun" in code.co_consts def test_generator_code_consts(): @@ -232,16 +226,17 @@ def g(): x = g() assert g.__code__ is x.gi_code -def dedup(lst, prev=object()): - for item in lst: - if item != prev: - yield item - prev = item - def check_lines(func): co = func.__code__ - lines = [line for _, _, line in co.co_lines()] - assert lines == list(dedup(lines)) + prev_line = object() # None is a valid line value. + prev_end = 0 + for start, end, line in co.co_lines(): + assert start <= end + assert line != prev_line + assert line is None or line > 0 + assert prev_end == start + prev_line = line + prev_end = end def test_check_lines_dedup(): def misshappen(): @@ -283,5 +278,35 @@ def bug93662(): ).strip() raise ValueError() + def singleline(): return 42 + check_lines(misshappen) - check_lines(bug93662) \ No newline at end of file + check_lines(bug93662) + check_lines(singleline) + + +def test_code_identity(): + import sys + import marshal + import types + import _imp + def foo(): + def bar(): + return sys._getframe() + + return bar + + bar = foo() + assert bar.__code__ is foo().__code__ + i = foo.__code__.co_consts.index(bar.__code__) + assert bar.__code__ is foo.__code__.co_consts[i] + assert bar.__code__ is bar().f_code + + foo_copy = types.FunctionType(marshal.loads(marshal.dumps(foo.__code__)), globals=foo.__globals__, closure=foo.__closure__) + bar_copy = foo_copy() + assert foo_copy.__code__ is not foo.__code__ + _imp._fix_co_filename(foo_copy.__code__, 'asdf') + assert foo_copy.__code__.co_filename == 'asdf' + assert bar_copy.__code__.co_filename == 'asdf' + assert foo.__code__.co_filename != 'asdf' + assert bar.__code__.co_filename != 'asdf' diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_codecs.py b/graalpython/com.oracle.graal.python.test/src/tests/test_codecs.py index 0bcf61237f..ed8071169d 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_codecs.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_codecs.py @@ -1,8 +1,11 @@ -# Copyright (c) 2019, 2025, Oracle and/or its affiliates. +# Copyright (c) 2019, 2026, Oracle and/or its affiliates. # Copyright (C) 1996-2017 Python Software Foundation # # Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +import importlib +import importlib.util import sys +from pathlib import Path def coding_checker(self, coder): @@ -76,6 +79,23 @@ def test_encode(): assert codecs.encode('[]', 'ascii') == b'[]' +def test_unicode_error_large_positions(): + large = 18977273910 + negative = -58 + + translate = UnicodeTranslateError('worl', large, negative, 'worldworldworldworldworldworldworldworldworldworldworld') + assert translate.args == ('worl', large, negative, 'worldworldworldworldworldworldworldworldworldworldworld') + assert str(translate) == "can't translate characters in position 18977273910--59: worldworldworldworldworldworldworldworldworldworldworld" + + encode = UnicodeEncodeError('utf-8', 'worl', large, negative, 'boom') + assert encode.args == ('utf-8', 'worl', large, negative, 'boom') + assert str(encode) == "'utf-8' codec can't encode characters in position 18977273910--59: boom" + + decode = UnicodeDecodeError('utf-8', b'worl', large, negative, 'boom') + assert decode.args == ('utf-8', b'worl', large, negative, 'boom') + assert str(decode) == "'utf-8' codec can't decode bytes in position 18977273910--59: boom" + + import codecs import unittest @@ -875,6 +895,34 @@ def test_encode_dict_err_xmlcharrefreplace(self): class MultibyteCodecTest(unittest.TestCase): + def test_missing_multibyte_codecs_raise_lookup_error(self): + for module_name in ('_codecs_cn', '_codecs_hk', '_codecs_iso2022', '_codecs_jp', '_codecs_kr', '_codecs_tw'): + with self.subTest(module_name=module_name): + module = importlib.import_module(module_name) + self.assertRaises(LookupError, module.getcodec, '__missing_codec__') + + def test_unsupported_multibyte_codec_modules_raise_import_error_on_graalpy(self): + encodings_dir = Path(__file__).resolve().parents[3] / 'lib-python' / '3' / 'encodings' + for module_name in ( + 'encodings.euc_jis_2004', + 'encodings.euc_jisx0213', + 'encodings.iso2022_jp_1', + 'encodings.iso2022_jp_2004', + 'encodings.iso2022_jp_3', + 'encodings.iso2022_jp_ext', + ): + with self.subTest(module_name=module_name): + module_path = encodings_dir / f'{module_name.rsplit(".", 1)[1]}.py' + spec = importlib.util.spec_from_file_location(f'test_{module_name.replace(".", "_")}', module_path) + module = importlib.util.module_from_spec(spec) + if sys.implementation.name == 'graalpy': + self.assertRaises(ImportError, spec.loader.exec_module, module) + else: + spec.loader.exec_module(module) + + def test_shift_jis_2004_codec_module_imports(self): + import encodings.shift_jis_2004 + # just a smoke test def test_encode(self): import _codecs_tw diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py b/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py index e4e19b88d7..e1b6ea9ec9 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_complex.py @@ -1,4 +1,4 @@ -# 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 @@ -39,6 +39,7 @@ import sys import unittest from math import atan2 +import math def test_create(): c = 4 + 4j @@ -91,6 +92,46 @@ def test_sub(): assert (1.5 - c2) == complex(-0.5, -2) +def test_mixed_real_complex_zero_signs(): + z = complex(0.0, -0.0) + + def add_int_local(): + x = 0 + return z + x, x + z + + def add_float_local(): + x = 0.0 + return z + x, x + z + + def add_bool_local(): + x = False + return z + x, x + z + + for left, right in (add_int_local(), add_float_local(), add_bool_local()): + assert left == right + assert math.copysign(1.0, left.imag) == 1.0 + assert math.copysign(1.0, right.imag) == 1.0 + + def sub_int_local(): + x = 0 + return x - z, z - x + + def sub_float_local(): + x = 0.0 + return x - z, z - x + + def sub_bool_local(): + x = False + return x - z, z - x + + # Subtraction is not commutative, but sign handling should follow CPython. + for left, right in (sub_int_local(), sub_float_local(), sub_bool_local()): + assert left == 0j + assert right == -0j + assert math.copysign(1.0, left.imag) == 1.0 + assert math.copysign(1.0, right.imag) == -1.0 + + def test_div(): c2 = 2+2j assert (c2 / 2) == complex(1, 1) @@ -187,7 +228,7 @@ def __index__(self): c = complex(CP1(5+5j), 7+7j) assert c == complex(-2, 12) assert type(c) == complex - + c = complex(CP1(5+5j), CP1(7+7j)) assert c == complex(-2, 12) assert type(c) == complex @@ -203,7 +244,7 @@ def __index__(self): c = CP1(CP1(5+5j), 7+7j) assert c == complex(-2, 12) assert type(c) == CP1 - + c = CP1(CP1(5+5j), CP1(7+7j)) assert c == complex(-2, 12) assert type(c) == CP1 @@ -215,7 +256,7 @@ def __index__(self): c = CP1(CP2(5+5j), 7+7j) assert c == complex(-7, 49) assert type(c) == CP1 - + c = CP1(CP2(5+5j), CP2(7+7j)) assert c == complex(-7, 49) assert type(c) == CP1 @@ -227,7 +268,7 @@ def __index__(self): c = complex(CP2(5+5j), 7+7j) assert c == complex(-7, 49) assert type(c) == complex - + c = complex(CP2(5+5j), CP2(7+7j)) assert c == complex(-7, 49) assert type(c) == complex @@ -243,7 +284,7 @@ def __index__(self): c = CP2(CP2(5+5j), 7+7j) assert c == complex(-7, 49) assert type(c) == CP2 - + c = CP2(CP2(5+5j), CP2(7+7j)) assert c == complex(-7, 49) assert type(c) == CP2 @@ -252,6 +293,17 @@ def __index__(self): if sys.version_info >= (3, 8, 0): assert complex(CP4()) == complex(123) + +def test_complex_subclass_without_dunder_complex_uses_fallback(): + class SubComplex(complex): + pass + + value = SubComplex(2 + 3j) + result = complex(value) + + assert result == 2 + 3j + assert type(result) is complex + class ComplexTest(unittest.TestCase): def assertAlmostEqual(self, a, b): @@ -442,7 +494,7 @@ def __complex__(self): return None self.assertEqual(complex(complex0(1j)), 42j) - # TODO we are not able to throw warning now. + # TODO we are not able to throw warning now. # with self.assertWarns(DeprecationWarning): self.assertEqual(complex(complex1(1j)), 2j) self.assertRaises(TypeError, complex, complex2(1j)) 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..d81c6446dc 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 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_datetime.py b/graalpython/com.oracle.graal.python.test/src/tests/test_datetime.py index 68835c0ea2..03d18c7258 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_datetime.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_datetime.py @@ -38,6 +38,11 @@ # SOFTWARE. import datetime +import os +import subprocess +import sys +import textwrap +import time import unittest class DateTest(unittest.TestCase): @@ -542,12 +547,29 @@ def test_strptime(self): actual = datetime.datetime.strptime("+00:00 GMT", "%z %Z") self.assertEqual(actual.tzinfo.tzname(None), "GMT") - import time timezone_name = time.localtime().tm_zone self.assertIsNotNone(timezone_name) actual = datetime.datetime.strptime(f"+00:00 {timezone_name}", "%z %Z") self.assertEqual(actual.tzinfo.tzname(None), timezone_name) + if hasattr(time, "tzset") and sys.executable: + proc = subprocess.run( + [sys.executable, "-c", textwrap.dedent("""\ + import datetime + import time + + time.tzset() + timezone_name = time.localtime().tm_zone + actual = datetime.datetime.strptime(f"+00:00 {timezone_name}", "%z %Z") + assert actual.tzinfo.tzname(None) == timezone_name + """)], + env={**os.environ, "TZ": "Etc/GMT-1"}, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + self.assertEqual(proc.returncode, 0, proc.stderr) + # time zone name without utc offset is ignored actual = datetime.datetime.strptime("UTC", "%Z") self.assertIsNone(actual.tzinfo) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_deque.py b/graalpython/com.oracle.graal.python.test/src/tests/test_deque.py index 251eae520e..86b7069d3f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_deque.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_deque.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2021, 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 @@ -394,6 +394,17 @@ def test_delitem(self): self.assertTrue(val not in d) self.assertEqual(len(d), 0) + def test_negative_index_out_of_range(self): + # GH-923: an index < -len must raise IndexError, not wrap around twice + d = deque([10, 20, 30, 40, 50]) + i = -len(d) - 1 + with self.assertRaises(IndexError): + d[i] + with self.assertRaises(IndexError): + d[i] = 0 + with self.assertRaises(IndexError): + del d[i] + def test_reverse(self): n = 500 # O(n**2) test, don't make this too big data = [random.random() for i in range(n)] diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_dict.py b/graalpython/com.oracle.graal.python.test/src/tests/test_dict.py index e96723941d..314855f55a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_dict.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_dict.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 @@ -38,6 +38,9 @@ # SOFTWARE. import unittest, sys +from collections import defaultdict + +graalpy_only = unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific dict storage test") def assert_raises(err, fn, *args, **kwargs): raised = False @@ -47,7 +50,6 @@ def assert_raises(err, fn, *args, **kwargs): raised = True assert raised - def test_equality(): class EqualTo: @@ -272,6 +274,225 @@ def __len__(self): assert set(d.keys()) == key_set, "unexpected keys: %s" % str(d.keys()) assert set(d.values()) == {97, 98, 99, 100}, "unexpected values: %s" % str(d.values()) + +@graalpy_only +def test_economic_map_storage_small_dict_grows_cleanly(): + d = {} + for i in range(4): + d[i] = i + + assert list(d.items()) == [(0, 0), (1, 1), (2, 2), (3, 3)] + + d[4] = 4 + + assert list(d.items()) == [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)] + + del d[1] + d[1] = 10 + assert list(d.items()) == [(0, 0), (2, 2), (3, 3), (4, 4), (1, 10)] + + +def test_economic_map_storage_small_dict_hash_and_eq_counts(): + count_hash = 0 + count_eq = 0 + + class Key: + def __init__(self, x): + self.x = x + + def __hash__(self): + nonlocal count_hash + count_hash += 1 + return 12345 + + def __eq__(self, other): + nonlocal count_eq + count_eq += 1 + return isinstance(other, Key) and self.x == other.x + + a = Key(1) + d = {0: 0, 1: 1, 2: 2, 3: 3, a: 42} + b = Key(1) + count_hash = 0 + count_eq = 0 + + assert b in d + assert count_hash == 1, count_hash + assert count_eq == 1, count_eq + + assert d[b] == 42 + assert count_hash == 2, count_hash + assert count_eq == 2, count_eq + + d[b] = 112 + assert count_hash == 3, count_hash + assert count_eq == 3, count_eq + + del d[b] + assert count_hash == 4, count_hash + assert count_eq == 4, count_eq + assert a not in d + + +def test_economic_map_storage_small_dict_copy_does_not_rehash_keys(): + count_hash = 0 + count_eq = 0 + + class Key: + def __init__(self, x): + self.x = x + + def __hash__(self): + nonlocal count_hash + count_hash += 1 + return self.x + + def __eq__(self, other): + nonlocal count_eq + count_eq += 1 + return isinstance(other, Key) and self.x == other.x + + d = {Key(i): i for i in range(5)} + count_hash = 0 + count_eq = 0 + + copied = d.copy() + assert copied == d + assert count_hash == 0, count_hash + assert count_eq == 0, count_eq + + rebuilt = dict(d) + assert rebuilt == d + assert count_hash == 0, count_hash + assert count_eq == 0, count_eq + + +def test_economic_map_storage_hash_and_eq_counts(): + count_hash = 0 + count_eq = 0 + + class Key: + def __init__(self, x): + self.x = x + + def __hash__(self): + nonlocal count_hash + count_hash += 1 + return 1234567 + + def __eq__(self, other): + nonlocal count_eq + count_eq += 1 + return isinstance(other, Key) and self.x == other.x + + a = Key(1) + d = {i: i for i in range(8)} + d[a] = 42 + + b = Key(1) + count_hash = 0 + count_eq = 0 + + assert b in d + assert count_hash == 1, count_hash + assert count_eq == 1, count_eq + + assert d[b] == 42 + assert count_hash == 2, count_hash + assert count_eq == 2, count_eq + + d[b] = 112 + assert count_hash == 3, count_hash + assert count_eq == 3, count_eq + + del d[b] + assert count_hash == 4, count_hash + assert count_eq == 4, count_eq + assert a not in d + + +def test_economic_map_storage_copy_does_not_rehash_keys(): + count_hash = 0 + count_eq = 0 + + class Key: + def __init__(self, x): + self.x = x + + def __hash__(self): + nonlocal count_hash + count_hash += 1 + return self.x + 1000 + + def __eq__(self, other): + nonlocal count_eq + count_eq += 1 + return isinstance(other, Key) and self.x == other.x + + d = {Key(i): i for i in range(9)} + + count_hash = 0 + count_eq = 0 + + copied = d.copy() + assert copied == d + assert count_hash == 0, count_hash + assert count_eq == 0, count_eq + + rebuilt = dict(d) + assert rebuilt == d + assert count_hash == 0, count_hash + assert count_eq == 0, count_eq + + +def test_economic_map_storage_compaction_preserves_order_and_hashes(): + count_hash = 0 + count_eq = 0 + + class Key: + def __init__(self, x): + self.x = x + + def __hash__(self): + nonlocal count_hash + count_hash += 1 + return 987654321 + + def __eq__(self, other): + nonlocal count_eq + count_eq += 1 + return isinstance(other, Key) and self.x == other.x + + original = Key(1) + d = {i: i for i in range(11)} + d[original] = 99 + + for i in range(4): + del d[i] + + count_hash = 0 + count_eq = 0 + del d[Key(1)] + assert count_hash == 1, count_hash + assert count_eq == 1, count_eq + + replacement = Key(1) + count_hash = 0 + count_eq = 0 + d[replacement] = 100 + assert count_hash == 1, count_hash + assert count_eq == 0, count_eq + + assert list(d.items()) == [(4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (replacement, 100)] + + probe = Key(1) + count_hash = 0 + count_eq = 0 + assert d[probe] == 100 + assert count_hash == 1, count_hash + assert count_eq == 1, count_eq + + def test_init6(): try: dict(1) @@ -357,6 +578,38 @@ def test_copy(): assert set(d1.keys()) == {'a', 'b', 'c'} +def test_defaultdict_operations_subclass_preserve_type(): + class DefaultDictSubclass(defaultdict): + pass + + d = DefaultDictSubclass(int, a=1) + copied = d.copy() + merged = d | {"b": 2} + rmerged = {"b": 2} | d + + assert type(copied) is DefaultDictSubclass + assert copied.default_factory is int + assert dict(copied) == {"a": 1} + assert type(merged) is DefaultDictSubclass + assert merged.default_factory is int + assert dict(merged) == {"a": 1, "b": 2} + assert type(rmerged) is DefaultDictSubclass + assert rmerged.default_factory is int + assert dict(rmerged) == {"b": 2, "a": 1} + + +def test_dict_operations_return_builtin_dict_for_subclass(): + class DictSubclass(dict): + pass + + d = DictSubclass(a=1) + other = {"b": 2} + + assert type(d.copy()) is dict + assert type(d | other) is dict + assert type(other | d) is dict + + def test_keywords(): def modifying(**kwargs): kwargs["a"] = 10 @@ -893,6 +1146,45 @@ def __eq__(self, other): assert count_eq == 1, count_eq +def test_hash_and_eq_for_keywords_storage(): + count_hash = 0 + count_eq = 0 + + class Probe: + def __hash__(self): + nonlocal count_hash + count_hash += 1 + return hash("a") + + def __eq__(self, other): + nonlocal count_eq + count_eq += 1 + return other == "a" + + def check(**kwargs): + probe = Probe() + + assert probe in kwargs + assert count_hash == 1, count_hash + assert count_eq == 1, count_eq + + assert kwargs[probe] == 1 + assert count_hash == 2, count_hash + assert count_eq == 2, count_eq + + kwargs[probe] = 2 + assert kwargs["a"] == 2 + assert count_hash == 3, count_hash + assert count_eq == 3, count_eq + + del kwargs[probe] + assert "a" not in kwargs + assert count_hash == 4, count_hash + assert count_eq == 4, count_eq + + check(a=1) + + def test_hash_and_eq_for_dynamic_object_storage(): class MyObject: def __init__(self, string): @@ -923,6 +1215,47 @@ def __hash__(self): del d2[MyObject("1")] assert "1" not in d2 + +def test_hash_and_eq_count_for_dynamic_object_storage(): + count_hash = 0 + count_eq = 0 + + class Probe: + def __hash__(self): + nonlocal count_hash + count_hash += 1 + return hash("a") + + def __eq__(self, other): + nonlocal count_eq + count_eq += 1 + return other == "a" + + class MyObject: + pass + + d = MyObject().__dict__ + d["a"] = 1 + probe = Probe() + + assert probe in d + assert count_hash == 1, count_hash + assert count_eq == 1, count_eq + + assert d[probe] == 1 + assert count_hash == 2, count_hash + assert count_eq == 2, count_eq + + d[probe] = 2 + assert d["a"] == 2 + assert count_hash == 3, count_hash + assert count_eq == 3, count_eq + + del d[probe] + assert "a" not in d + assert count_hash == 4, count_hash + assert count_eq == 4, count_eq + def test_update_side_effect_on_other(): class X: def __hash__(self): @@ -1249,6 +1582,46 @@ def test_eq_side_effects(): assert key.eq_calls == 1 +def test_keys_xor_side_effects(): + key1 = TrackingKey('foo') + key2 = TrackingKey('foo') + d1 = {key1: 1} + d2 = {key2: 2} + key1.clear_observations() + key2.clear_observations() + + assert d1.keys() ^ d2.keys() == set() + assert key1.eq_calls == 1 + assert key1.hash_calls == 0 + assert key2.eq_calls == 0 + assert key2.hash_calls == 1 + + +def test_items_xor_side_effects(): + log = [] + + class Key: + def __init__(self, name): + self.name = name + + def __hash__(self): + log.append(("hash", self.name)) + return 42 + + def __eq__(self, other): + log.append(("eq", self.name, other.name)) + return True + + key1 = Key('left') + key2 = Key('right') + d1 = {key1: 1} + d2 = {key2: 1} + log.clear() + + assert d1.items() ^ d2.items() == set() + assert log == [("eq", "left", "right"), ("eq", "left", "right")] + + # TODO: GR-40680 # def test_iteration_and_del(): # def test_iter(get_iterable): @@ -1290,4 +1663,3 @@ class Test: del o.foo assert "foo" not in o.__dict__ - diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_entropy_subprocess.py b/graalpython/com.oracle.graal.python.test/src/tests/test_entropy_subprocess.py new file mode 100644 index 0000000000..0016e3b083 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_entropy_subprocess.py @@ -0,0 +1,323 @@ +# 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 os +import subprocess +import sys +import tempfile +import textwrap +import threading +import unittest + +from tests.util import needs_capi + + +@unittest.skipUnless(sys.implementation.name == "graalpy" and sys.platform.startswith("linux"), "Linux GraalPy-specific test") +class EntropySubprocessTests(unittest.TestCase): + HASH_SECRET_BYTES = 24 + RANDOM_SEED_BYTES = 624 * 4 + RANDOM_INSTANCE_BYTES = HASH_SECRET_BYTES + RANDOM_SEED_BYTES + RANDOM_MODULE_BYTES = HASH_SECRET_BYTES + (2 * RANDOM_SEED_BYTES) + TEMPFILE_CANDIDATE_NAME_BYTES = HASH_SECRET_BYTES + (4 * RANDOM_SEED_BYTES) + SSL_DATA_DIR = os.path.join(os.path.dirname(__file__), "ssldata") + + @staticmethod + def _graal_log_args(): + args = [f"--log.file={os.devnull}"] + if not __graalpython__.is_native: + args.append(f"--vm.Djdk.graal.LogFile={os.devnull}") + return args + + def _run_with_init_pipe(self, byte_count: int, code: str): + with tempfile.TemporaryDirectory() as temp_dir: + path = os.path.join(temp_dir, "initrandom") + subprocess.run(["mkfifo", path], check=True) + writer_error = [] + bytes_written = 0 + + def feed_pipe(): + nonlocal bytes_written + try: + fd = os.open(path, os.O_WRONLY) + try: + remaining = byte_count + chunk = b"\x00" * min(4096, byte_count) + while remaining > 0: + written = os.write(fd, chunk[:remaining]) + bytes_written += written + remaining -= written + finally: + os.close(fd) + except BaseException as exc: # re-raised in the main thread + writer_error.append(exc) + + writer = threading.Thread(target=feed_pipe) + writer.start() + try: + result = self._run_with_init_source(f"device:{path}", code) + finally: + writer.join(timeout=10) + if writer.is_alive(): + self.fail("initrandom pipe writer thread did not finish") + if writer_error: + raise writer_error[0] + return result, bytes_written + + def _run_with_init_source(self, source: str, code: str): + env = os.environ.copy() + env.pop("PYTHONHASHSEED", None) + return subprocess.run( + [ + sys.executable, + "-S", + "--experimental-options=true", + *self._graal_log_args(), + f"--python.InitializationEntropySource={source}", + "-c", + code, + ], + capture_output=True, + text=True, + env=env, + ) + + def assert_initrandom_exhausted(self, result): + self.assertNotEqual(0, result.returncode, result) + combined = f"{result.stdout}\n{result.stderr}" + self.assertIn("initialization entropy device exhausted", combined) + + def assert_subprocess_ok(self, result): + self.assertEqual(0, result.returncode, result) + + def assert_initrandom_bytes_used(self, byte_count: int, code: str, stdout: str): + result, bytes_written = self._run_with_init_pipe(byte_count, code) + self.assert_subprocess_ok(result) + self.assertEqual(byte_count, bytes_written) + self.assertEqual(stdout, result.stdout.strip()) + + exhausted_result, exhausted_written = self._run_with_init_pipe(byte_count - 1, code) + self.assert_initrandom_exhausted(exhausted_result) + self.assertEqual(byte_count - 1, exhausted_written) + + def test_startup_hash_secret_uses_initrandom(self): + self.assert_initrandom_bytes_used(self.HASH_SECRET_BYTES, "print('ok')", "ok") + + def test__random_random_uses_initrandom(self): + self.assert_initrandom_bytes_used( + self.RANDOM_INSTANCE_BYTES, + "import _random; _random.Random(); print('ok')", + "ok", + ) + + def test_random_module_import_uses_initrandom(self): + self.assert_initrandom_bytes_used( + self.RANDOM_MODULE_BYTES, + "import random; print('ok')", + "ok", + ) + + def test_systemrandom_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.RANDOM_MODULE_BYTES, + "import random; random.SystemRandom().getrandbits(32); print('ok')", + "ok", + ) + + def test_secrets_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.RANDOM_MODULE_BYTES, + "import random; import secrets; secrets.token_hex(8); print('ok')", + "ok", + ) + + def test_os_urandom_does_not_use_initrandom(self): + self.assert_initrandom_bytes_used( + self.HASH_SECRET_BYTES, + "import os; print(len(os.urandom(16)))", + "16", + ) + + def test_multiprocessing_process_import_does_not_use_initrandom(self): + self.assert_initrandom_bytes_used( + self.HASH_SECRET_BYTES, + "import multiprocessing.process; print('ok')", + "ok", + ) + + def test_multiprocessing_deliver_challenge_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.RANDOM_MODULE_BYTES, + textwrap.dedent(""" + import random + import multiprocessing.connection as mc + auth = b'authkey' + + class Dummy: + def __init__(self): + self.sent = [] + + def send_bytes(self, data): + self.sent.append(data) + + def recv_bytes(self, _max): + msg = self.sent[-1][len(mc._CHALLENGE):] + return mc._create_response(auth, msg) + + d = Dummy() + mc.deliver_challenge(d, auth) + print('ok') + """), + "ok", + ) + + def test_tempfile_candidate_names_use_initrandom(self): + self.assert_initrandom_bytes_used( + self.TEMPFILE_CANDIDATE_NAME_BYTES, + "import tempfile; next(tempfile._get_candidate_names()); print('ok')", + "ok", + ) + + def test_tempfile_after_random_import_uses_initrandom(self): + self.assert_initrandom_bytes_used( + self.TEMPFILE_CANDIDATE_NAME_BYTES, + "import random; import tempfile; next(tempfile._get_candidate_names()); print('ok')", + "ok", + ) + + def test_email_generator_boundary_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.RANDOM_MODULE_BYTES, + "import random; from email.generator import Generator; Generator._make_boundary(); print('ok')", + "ok", + ) + + def test_imaplib_connect_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.RANDOM_MODULE_BYTES, + textwrap.dedent(""" + import random + import imaplib + + class Dummy(imaplib.IMAP4): + def open(self, host='', port=imaplib.IMAP4_PORT, timeout=None): + pass + + def _get_response(self): + self.untagged_responses = {'OK': [b'']} + return 'OK' + + def _get_capabilities(self): + self.capabilities = ('IMAP4REV1',) + + def shutdown(self): + pass + + d = Dummy.__new__(Dummy) + d.debug = imaplib.Debug + d.state = 'LOGOUT' + d.literal = None + d.tagged_commands = {} + d.untagged_responses = {} + d.continuation_response = '' + d.is_readonly = False + d.tagnum = 0 + d._tls_established = False + d._mode_ascii() + d._connect() + print('ok') + """), + "ok", + ) + + def test_pyexpat_import_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.HASH_SECRET_BYTES, + "import pyexpat; print(pyexpat.__name__)", + "pyexpat", + ) + + def test_pyexpat_parsercreate_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.HASH_SECRET_BYTES, + "import pyexpat; p = pyexpat.ParserCreate(); print(type(p).__name__)", + "xmlparser", + ) + + @needs_capi + def test_sqlite3_import_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.HASH_SECRET_BYTES, + "import _sqlite3; print(_sqlite3.__name__)", + "_sqlite3", + ) + + @needs_capi + def test_sqlite3_randomblob_does_not_use_initrandom(self): + self.assert_initrandom_bytes_used( + self.HASH_SECRET_BYTES, + "import sqlite3; " + "conn = sqlite3.connect(':memory:'); " + "print(conn.execute('select length(randomblob(16))').fetchone()[0])", + "16", + ) + + def test_ssl_load_cert_chain_does_not_use_initrandom(self): + cert = os.path.join(self.SSL_DATA_DIR, "signed_cert.pem") + self.assert_initrandom_bytes_used( + self.HASH_SECRET_BYTES, + f"import ssl; " + f"ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER); " + f"ctx.load_cert_chain(r'{cert}'); " + f"print('ok')", + "ok", + ) + + def test_uuid1_does_not_use_additional_initrandom(self): + self.assert_initrandom_bytes_used( + self.RANDOM_MODULE_BYTES, + "import random; import uuid; uuid.uuid1(node=1); print('ok')", + "ok", + ) + + def test_uuid4_does_not_use_initrandom(self): + self.assert_initrandom_bytes_used( + self.HASH_SECRET_BYTES, + "import uuid; print(uuid.uuid4().version)", + "4", + ) 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_fcntl.py b/graalpython/com.oracle.graal.python.test/src/tests/test_fcntl.py index bc59641ce8..2f6ea64bdf 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_fcntl.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_fcntl.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, 2024, 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 @@ -62,22 +62,27 @@ def log(msg): # print(msg) pass -def python_flock_blocks_sh_flock(python_flock_type, sh_flock_type): +def python_flock_interacts_with_sh_flock(python_flock_type, sh_flock_type, should_block): os.close(os.open(TEST_FILENAME_FULL_PATH, os.O_WRONLY | os.O_CREAT)) file = os.open(TEST_FILENAME_FULL_PATH, os.O_WRONLY) + p = None try: fcntl.flock(file, python_flock_type) p = subprocess.Popen("flock -%s %s -c 'exit 42'" % (sh_flock_type, TEST_FILENAME_FULL_PATH), shell=True) - log("sleeping...") - time.sleep(0.25) - assert p.poll() is None # the process should be still waiting for the lock - log("unlocking the file...") - fcntl.flock(file, fcntl.LOCK_UN) # release the lock + if should_block: + log("sleeping...") + time.sleep(0.25) + assert p.poll() is None # the process should be still waiting for the lock + log("unlocking the file...") + fcntl.flock(file, fcntl.LOCK_UN) # release the lock log("checking the retcode...") - time.sleep(0.25) - assert p.poll() == 42 - log(f"{p.returncode=}") + retcode = p.wait(timeout=5) + assert retcode == 42 + log(f"{retcode=}") finally: + if p is not None and p.poll() is None: + p.terminate() + p.wait(timeout=2) fcntl.flock(file, fcntl.LOCK_UN) os.close(file) @@ -85,32 +90,23 @@ class FcntlTests(unittest.TestCase): @unittest.skipUnless(__graalpython__.posix_module_backend() != 'java', 'No support in Truffle API (GR-28740)') @unittest.skipUnless(sys.platform != 'darwin', 'MacOSX does not have flock utility') def test_flock_x_and_x(self): - python_flock_blocks_sh_flock(fcntl.LOCK_EX, 'x') + python_flock_interacts_with_sh_flock(fcntl.LOCK_EX, 'x', should_block=True) @unittest.skipUnless(__graalpython__.posix_module_backend() != 'java', 'No support in Truffle API (GR-28740)') @unittest.skipUnless(sys.platform != 'darwin', 'MacOSX does not have flock utility') def test_flock_x_and_s(self): - python_flock_blocks_sh_flock(fcntl.LOCK_EX, 's') + python_flock_interacts_with_sh_flock(fcntl.LOCK_EX, 's', should_block=True) @unittest.skipUnless(__graalpython__.posix_module_backend() != 'java', 'No support in Truffle API (GR-28740)') @unittest.skipUnless(sys.platform != 'darwin', 'MacOSX does not have flock utility') def test_flock_s_and_x(self): - python_flock_blocks_sh_flock(fcntl.LOCK_SH, 'x') + python_flock_interacts_with_sh_flock(fcntl.LOCK_SH, 'x', should_block=True) @unittest.skipUnless(__graalpython__.posix_module_backend() != 'java', 'No support in Truffle API (GR-28740)') @unittest.skipUnless(sys.platform != 'darwin', 'MacOSX does not have flock utility') @unittest.skipUnless("graalpython" in os.environ.get("BITBUCKET_REPO_URL", "graalpython"), "Do not run this in auxillary CI jobs, it can be flaky") def test_flock_s_and_s(self): - os.close(os.open(TEST_FILENAME_FULL_PATH, os.O_WRONLY | os.O_CREAT)) - file = os.open(TEST_FILENAME_FULL_PATH, os.O_WRONLY) - try: - fcntl.flock(file, fcntl.LOCK_SH) - p = subprocess.Popen("flock -s %s -c 'exit 42'" % TEST_FILENAME_FULL_PATH, shell=True) - time.sleep(0.25) - assert p.poll() == 42 - finally: - fcntl.flock(file, fcntl.LOCK_UN) - os.close(file) + python_flock_interacts_with_sh_flock(fcntl.LOCK_SH, 's', should_block=False) @unittest.skipUnless(sys.platform == 'linux', "Linux only test") diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_float.py b/graalpython/com.oracle.graal.python.test/src/tests/test_float.py index 1937d21be9..70f4895178 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_float.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_float.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 @@ -63,6 +63,13 @@ def test_is_integer(self): # for doubles this big, all representable values are integers... assert (2**52 + 0.5).is_integer() + def test_pow_overflow(self): + self.assertRaises(OverflowError, pow, 10.0, 400) + self.assertRaises(OverflowError, pow, 10.0, 400.0) + self.assertRaises(OverflowError, pow, -1e308, 3.0) + self.assertEqual(pow(INF, 2.0), INF) + self.assertEqual(pow(2.0, INF), INF) + def test_rounding(self): assert round(1.123, 0) == 1 assert round(1.123, 1) == 1.1 @@ -148,6 +155,23 @@ def __round__(x, n): a = object() assert round(C(), a) == a + def test_round_missing_special_method(self): + class MissingRound: + def __getattr__(self, name): + if name == "__round__": + return lambda *args: 42 + raise AttributeError(name) + + with self.assertRaisesRegex(TypeError, "__round__"): + round(MissingRound()) + + def test_round_returns_notimplemented(self): + class NotImplementedRound: + def __round__(self, *args): + return NotImplemented + + assert round(NotImplementedRound()) is NotImplemented + def test_create(self): class Obj: def __float__(self): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_frame_tests.py b/graalpython/com.oracle.graal.python.test/src/tests/test_frame_tests.py index 88b51fff09..64381542f8 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_frame_tests.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_frame_tests.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 @@ -222,30 +222,20 @@ class Foo: assert 'c' in locals() assert 'x' not in locals() -# GR-22089 -# def test_backref_from_traceback(): -# def bar(): -# raise RuntimeError -# -# def foo(): -# bar() -# -# try: -# foo() -# except Exception as e: -# assert e.__traceback__.tb_frame.f_back.f_code == sys._getframe(0).f_back.f_code -# assert e.__traceback__.tb_next.tb_next.tb_frame.f_back.f_code == foo.__code__ -# assert e.__traceback__.tb_next.tb_frame.f_back.f_code == test_backref_from_traceback.__code__ +def test_backref_from_traceback(): + def bar(): + raise RuntimeError -def test_clearing_globals(): - global foo, junk - foo = 123 - assert "foo" in globals().keys() - assert "junk" not in globals().keys() - junk = globals().clear() - assert "foo" not in globals().keys() - assert "junk" in globals().keys() + def foo(): + bar() + + try: + foo() + except Exception as e: + assert e.__traceback__.tb_frame.f_back.f_code == sys._getframe(0).f_back.f_code + assert e.__traceback__.tb_next.tb_next.tb_frame.f_back.f_code == foo.__code__ + assert e.__traceback__.tb_next.tb_frame.f_back.f_code == test_backref_from_traceback.__code__ def test_frame_from_another_thread(): @@ -278,3 +268,96 @@ def target(): assert frame.f_locals['b'] == 2 event4.set() thread.join(timeout=60) + + +OTHER_RUNNING_INNER = 'running_inner' +OTHER_RUNNING_OUTER = 'running_outer' +OTHER_TERMINATED = 'terminated' + + +def current_frames_includes_other_thread(test_case): + import sys, threading + + ready = threading.Event() + outer_ready = threading.Event() + release_inner = threading.Event() + release_outer = threading.Event() + worker_ident = None + + def target(): + nonlocal worker_ident + worker_ident = threading.get_ident() + def target_inner(): + my_local_var = 60 + ready.set() + release_inner.wait(timeout=my_local_var) + target_local_var = 13 + target_inner() + outer_ready.set() + release_outer.wait(60) + return target_local_var + + thread = threading.Thread(target=target) + thread.start() + try: + assert ready.wait(timeout=60) + frames = sys._current_frames() + if test_case == OTHER_TERMINATED: + release_inner.set() + release_outer.set() + thread.join(timeout=60) + elif test_case == OTHER_RUNNING_OUTER: + release_inner.set() + assert outer_ready.wait(timeout=60) + assert worker_ident in frames + + frame = frames[worker_ident] + if frame is None: + return # we hit the timeout + + def format_frame(frame): + code = frame.f_code + return f"{code.co_name}@{os.path.basename(code.co_filename)}" + + seen_frames = [] + while frame is not None and frame.f_code.co_name != "target_inner": + seen_frames.append(format_frame(frame)) + frame = frame.f_back + + message = f"test case: {test_case}; traversed frames: {', '.join(seen_frames) or ''}" + assert frame is not None, message + assert frame.f_code.co_name == "target_inner", f"{frame.f_code.co_name=}; {message}" + assert "target_inner" in repr(frame), f"{repr(frame)=}; {message}" + assert "my_local_var" in frame.f_locals, f"{frame.f_locals.keys()=}; {message}" + + frame = frame.f_back + assert frame is not None + assert "target_inner" not in repr(frame) + assert "target" in repr(frame) + assert frame.f_code.co_name == "target" + assert "target_local_var" in frame.f_locals + finally: + release_inner.set() + release_outer.set() + thread.join(timeout=60) + + +def test_current_frames_includes_other_thread_terminated(): + current_frames_includes_other_thread(OTHER_TERMINATED) + +def test_current_frames_includes_other_thread_in_inner(): + current_frames_includes_other_thread(OTHER_RUNNING_INNER) + +def test_current_frames_includes_other_thread_in_outer(): + current_frames_includes_other_thread(OTHER_RUNNING_OUTER) + + +# this must be the last test! +def test_clearing_globals(): + global foo, junk + foo = 123 + assert "foo" in globals().keys() + assert "junk" not in globals().keys() + junk = globals().clear() + assert "foo" not in globals().keys() + assert "junk" in globals().keys() diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py b/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py index 407ca7c8f4..4f4d6b6a32 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_functions.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_functions.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 @@ -192,6 +192,61 @@ def foo(*args, x=1): assert_raises(TypeError, foo) +def test_function_kwdefaults_dict_is_live(): + def foo(*, x=1): + return x + + kwdefaults = foo.__kwdefaults__ + assert kwdefaults is foo.__kwdefaults__ + kwdefaults["x"] = 2 + assert foo() == 2 + + assigned = {"x": 3} + foo.__kwdefaults__ = assigned + assert foo.__kwdefaults__ is assigned + assigned["x"] = 4 + assert foo() == 4 + + assigned[1] = "not a keyword" + assert foo.__kwdefaults__ == {"x": 4, 1: "not a keyword"} + assert foo() == 4 + + non_string_kwdefaults = {1: "not a keyword"} + foo.__kwdefaults__ = non_string_kwdefaults + assert foo.__kwdefaults__ is non_string_kwdefaults + assert_raises(TypeError, foo) + non_string_kwdefaults["x"] = 5 + assert foo() == 5 + + +def test_mangled_kwonly_kwdefaults_dict_is_live(): + class argmap: + def make_wrapper(self): + def func(*args, __wrapper=None, **kwargs): + return __wrapper + + assert "_argmap__wrapper" in func.__kwdefaults__ + func.__kwdefaults__["_argmap__wrapper"] = func + return func + + func = argmap().make_wrapper() + assert "_argmap__wrapper" in func.__code__.co_varnames + assert "__wrapper" not in func.__code__.co_varnames + assert func() is func + + +def test_method_kwdefaults_dict_is_live(): + class C: + def method(self, *, x=1): + return x + + obj = C() + kwdefaults = obj.method.__kwdefaults__ + assert kwdefaults is C.method.__kwdefaults__ + kwdefaults["x"] = 2 + assert obj.method() == 2 + + def test_code_change(): def foo(): return "foo" @@ -236,7 +291,7 @@ def assign_code(x, y): assert_raises(ValueError, assign_code, foo, foobar_code) bazbar.__code__ = foobar_code assert bazbar() == (2,3) - + def test_function_dict_writeable(): def foo(): pass diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_imports.py b/graalpython/com.oracle.graal.python.test/src/tests/test_imports.py index fd5420b03d..dc7bd6d7eb 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_imports.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_imports.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 @@ -146,6 +146,11 @@ def func(x): assert code.co_filename == old_name + '_more_path' +def test_imp_source_hash(): + import _imp + assert _imp.source_hash(123456789, b'hello!').hex() == '04e61e229a23a446' + + def test_recursive_import_from(): if sys.version_info.minor >= 6: import package.recpkg @@ -176,16 +181,16 @@ def test_circular_import_valid(): import time as package25274 #has to be in global space for the next test def test_local_property_25274(): - + def mytest(): assert len(locals()) == 0 import package25274.sub25274 assert 'package25274' in locals() assert package25274.top_property == 10 assert package25274.sub25274.sub_property == 20 - + mytest() assert hasattr(package25274, 'tzname') - + diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_indirect_call.py b/graalpython/com.oracle.graal.python.test/src/tests/test_indirect_call.py index c993d68ba5..052067ec3d 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_indirect_call.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_indirect_call.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 @@ -40,6 +40,7 @@ import os from tests.cpyext import CPyExtTestCase, CPyExtType +from tests.util import has_capi, needs_capi # synchronize with Java implementation of __graalpython__.indirect_call_tester TYPE_INDIRECT_BOUNDARY = 1 @@ -51,9 +52,9 @@ NUM_ITERATIONS = 1000000 if sys.implementation.name == "graalpy" else 5 # Because of splitting, the code may not stabilize after first iteration and we may -# see some stack walks during first few iterations. With default runtime or splitting -# disabled, these tests should pass with STABILIZES_AT=1 -STABILIZES_AT = int(os.environ.get('GRAALPY_TEST_INDIRECT_CALL_STABILIZES_AT', 10)) +# see some stack walks at later points. With default runtime or splitting +# disabled, these tests should pass with ALLOWED_NUM_STACK_WALKS=2 (uncached may add one more) +ALLOWED_NUM_STACK_WALKS = int(os.environ.get('GRAALPY_ALLOWED_NUM_STACK_WALKS', 6)) has_stack_walk_check = False if sys.implementation.name == "graalpy": @@ -70,15 +71,18 @@ def was_stack_walk(new_value): return False -IndirectCApiCallTester = CPyExtType( - 'IndirectCApiCallTester', - code=''' - static PyObject* IndirectCApiCallTester_call(PyObject* self, PyObject *callable) { - return PyObject_CallNoArgs(callable); - } - ''', - tp_methods='''{"call", (PyCFunction)IndirectCApiCallTester_call, METH_O, ""}''', -) +if has_capi(): + IndirectCApiCallTester = CPyExtType( + 'IndirectCApiCallTester', + code=''' + static PyObject* IndirectCApiCallTester_call(PyObject* self, PyObject *callable) { + return PyObject_CallNoArgs(callable); + } + ''', + tp_methods='''{"call", (PyCFunction)IndirectCApiCallTester_call, METH_O, ""}''', + ) +else: + IndirectCApiCallTester = None # === capturing frame @@ -90,14 +94,13 @@ def fun(x): known_local = x return forwarding_call(callee, *args) + stack_walks = 0 for i in range(NUM_ITERATIONS): assert fun(i * 2).f_locals['known_local'] == i * 2 - if i <= STABILIZES_AT: - # just reset the flag - was_stack_walk(False) - else: - assert not was_stack_walk(False), f"{i=}" + stack_walks += was_stack_walk(False) + assert stack_walks <= ALLOWED_NUM_STACK_WALKS, f"Too many stack walks recorded: {stack_walks}" +@needs_capi def test_capi_get_frame(): check_get_frame_no_deopt(IndirectCApiCallTester().call) @@ -128,13 +131,11 @@ def __str__(self): def test_format_with_get_frame_in_str(): secret_var = 42 + stack_walks = 0 for i in range(NUM_ITERATIONS): assert "{}".format(Var("secret_var")) == "42" - if i <= STABILIZES_AT: - # just reset the flag - was_stack_walk(False) - else: - assert not was_stack_walk(False), f"{i=}" + stack_walks += was_stack_walk(False) + assert stack_walks <= ALLOWED_NUM_STACK_WALKS, f"Too many stack walks recorded: {stack_walks}" escape_frame = None class AttrGetter: @@ -146,13 +147,11 @@ def __getattribute__(self, name): def test_obj_with_get_frame_in_getattribute(): secret_var = 42 + stack_walks = 0 for i in range(NUM_ITERATIONS): assert AttrGetter().secret_var == 42 - if i <= STABILIZES_AT: - # just reset the flag - was_stack_walk(False) - else: - assert not was_stack_walk(False), f"{i=}" + stack_walks += was_stack_walk(False) + assert stack_walks <= ALLOWED_NUM_STACK_WALKS, f"Too many stack walks recorded: {stack_walks}" # === exception state @@ -174,16 +173,13 @@ def check_ex(msg): assert type(ex) == IndexError, f"{ex=}, {msg=}" assert str(ex) == str(msg), f"{ex=}, {msg=}" + stack_walks = 0 for i in range(NUM_ITERATIONS): check_ex(i*2) - if i <= max(STABILIZES_AT, 2): - # uncached interpreter always passes exception down, so on first call we do not walk the stack - # and do not invalidate the assumptions. We can proactively walk the stack just to invalidate - # the assumptions if we ever find that this is a problem. - was_stack_walk(False) - else: - assert not was_stack_walk(False) + stack_walks += was_stack_walk(False) + assert stack_walks <= ALLOWED_NUM_STACK_WALKS, f"Too many stack walks recorded: {stack_walks}" +@needs_capi def test_capi_get_ex(): check_get_ex_no_deopt(IndirectCApiCallTester().call) @@ -209,16 +205,14 @@ def __str__(self): return str(self.escape_ex) def test_format_with_get_ex_in_str(): + stack_walks = 0 for i in range(NUM_ITERATIONS): try: raise IndexError(str(i)) except: assert "{}".format(ExStr()) == str(i) - if i <= max(STABILIZES_AT, 2): - # see similar code above - was_stack_walk(False) - else: - assert not was_stack_walk(False) + stack_walks += was_stack_walk(False) + assert stack_walks <= ALLOWED_NUM_STACK_WALKS, f"Too many stack walks recorded: {stack_walks}" escape_ex = None class AttrGetterFromEx: @@ -229,13 +223,11 @@ def __getattribute__(self, name): def test_obj_with_get_ex_in_getattribute(): + stack_walks = 0 for i in range(NUM_ITERATIONS): try: raise IndexError(str(i)) except: assert AttrGetterFromEx().dummy_attribute == str(i) - if i <= max(STABILIZES_AT, 2): - # see similar code above - was_stack_walk(False) - else: - assert not was_stack_walk(False) + stack_walks += was_stack_walk(False) + assert stack_walks <= ALLOWED_NUM_STACK_WALKS, f"Too many stack walks recorded: {stack_walks}" diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_int.py b/graalpython/com.oracle.graal.python.test/src/tests/test_int.py index cc3ead0f84..cc2810a67b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_int.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_int.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 @@ -703,6 +703,13 @@ def __bytes__(self): self.assertEqual(int.from_bytes(mybyteslike(), 'big'), 2580) + def test_from_object_without_bytes_uses_iterable_fallback(self): + class IterableOnly: + def __iter__(self): + return iter([1, 2, 3]) + + self.assertEqual(int.from_bytes(IterableOnly(), 'big'), 0x010203) + def test_from_wrong_byteslike_object(self): class mybyteslike1(): def __bytes__(self): @@ -846,6 +853,15 @@ def test_WrongInput(self): self.assertRaises(OverflowError, (256).to_bytes, 1, 'big', signed=True) self.assertRaises(OverflowError, (256).to_bytes, 1, 'little', signed=False) self.assertRaises(OverflowError, (256).to_bytes, 1, 'little', signed=True) + self.assertRaises(OverflowError, (32768).to_bytes, 2, 'big', signed=True) + self.assertRaises(OverflowError, (32768).to_bytes, 2, 'little', signed=True) + self.assertRaises(OverflowError, (-32769).to_bytes, 2, 'big', signed=True) + self.assertRaises(OverflowError, (-32769).to_bytes, 2, 'little', signed=True) + self.assertRaises(OverflowError, (-5242881).to_bytes, 2, 'big', signed=True) + self.assertRaises(OverflowError, (-5242881).to_bytes, 2, 'little', signed=True) + self.assertRaises(OverflowError, self.MyInt(32768).to_bytes, 2, 'big', signed=True) + self.assertRaises(OverflowError, self.MyInt(-32769).to_bytes, 2, 'big', signed=True) + self.assertRaises(OverflowError, self.MyInt(-5242881).to_bytes, 2, 'big', signed=True) self.assertRaises(OverflowError, (-1).to_bytes, 2, 'big', signed=False) self.assertRaises(OverflowError, (-1).to_bytes, 2, 'little', signed=False) self.assertRaises(OverflowError, (1).to_bytes, 0, 'big') diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py index 8e586345f3..42f1789d5e 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_interop.py @@ -114,6 +114,8 @@ def test_single_trait_classes(self): polyglot.ForeignObject, polyglot.ForeignList, polyglot.ForeignBoolean, + polyglot.ForeignDate, + polyglot.ForeignDateTime, polyglot.ForeignException, polyglot.ForeignExecutable, polyglot.ForeignDict, @@ -124,6 +126,8 @@ def test_single_trait_classes(self): polyglot.ForeignNone, polyglot.ForeignNumber, polyglot.ForeignString, + polyglot.ForeignTime, + polyglot.ForeignTimeZone, ] for c in classes: @@ -131,7 +135,16 @@ def test_single_trait_classes(self): if c is polyglot.ForeignBoolean: self.assertIs(c.__base__, polyglot.ForeignNumber) elif c is not polyglot.ForeignObject: - self.assertIs(c.__base__, polyglot.ForeignObject) + if c is polyglot.ForeignDate: + self.assertIs(c.__base__, __import__("datetime").date) + elif c is polyglot.ForeignTime: + self.assertIs(c.__base__, __import__("datetime").time) + elif c is polyglot.ForeignDateTime: + self.assertIs(c.__base__, __import__("datetime").datetime) + elif c is polyglot.ForeignTimeZone: + self.assertIs(c.__base__, __import__("datetime").tzinfo) + else: + self.assertIs(c.__base__, polyglot.ForeignObject) def test_get_class(self): def wrap(obj): @@ -155,6 +168,7 @@ def t(obj): self.assertEqual(t("abc"), polyglot.ForeignString) from java.lang import Object, Boolean, Integer, Throwable, Thread, Number, String + from java.time import LocalDate, LocalDateTime, LocalTime, ZoneId from java.util import ArrayList, HashMap, ArrayDeque from java.math import BigInteger null = Integer.getInteger("something_that_does_not_exists") @@ -172,6 +186,19 @@ def t(obj): self.assertEqual(type(null), polyglot.ForeignNone) self.assertEqual(type(BigInteger.valueOf(42)), polyglot.ForeignNumber) self.assertEqual(type(wrap(String("abc"))), polyglot.ForeignString) + local_date = LocalDate.of(2025, 3, 23) + self.assertIsInstance(local_date, polyglot.ForeignDate) + self.assertIsInstance(local_date, __import__("datetime").date) + + local_time = LocalTime.of(7, 8, 9) + self.assertIsInstance(local_time, polyglot.ForeignTime) + self.assertIsInstance(local_time, __import__("datetime").time) + + local_date_time = LocalDateTime.of(2025, 3, 23, 7, 8, 9) + self.assertIsInstance(local_date_time, polyglot.ForeignDateTime) + self.assertIsInstance(local_date_time, __import__("datetime").datetime) + + self.assertEqual(type(ZoneId.of("UTC")), polyglot.ForeignTimeZone) def test_import(self): def some_function(): @@ -186,6 +213,146 @@ def some_function(): assert imported_fun1 is some_function assert imported_fun1() == "hello, polyglot world!" + def test_foreign_date_behavior(self): + import datetime + import java + + LocalDate = java.type("java.time.LocalDate") + + d = LocalDate.of(2025, 3, 23) + self.assertEqual(d.year, 2025) + self.assertEqual(d.month, 3) + self.assertEqual(d.day, 23) + self.assertEqual(str(d), "2025-03-23") + self.assertEqual(d.isoformat(), "2025-03-23") + self.assertEqual(d.ctime(), datetime.date(2025, 3, 23).ctime()) + self.assertEqual(d.strftime("%Y-%m-%d"), "2025-03-23") + self.assertEqual(format(d, "%Y-%m-%d"), "2025-03-23") + self.assertEqual(d.toordinal(), datetime.date(2025, 3, 23).toordinal()) + self.assertEqual(d.weekday(), datetime.date(2025, 3, 23).weekday()) + self.assertEqual(d.isoweekday(), datetime.date(2025, 3, 23).isoweekday()) + self.assertEqual(d.isocalendar(), datetime.date(2025, 3, 23).isocalendar()) + self.assertEqual(d.timetuple(), datetime.date(2025, 3, 23).timetuple()) + self.assertEqual(hash(d), hash(datetime.date(2025, 3, 23))) + self.assertEqual(d, datetime.date(2025, 3, 23)) + self.assertEqual(d, LocalDate.of(2025, 3, 23)) + self.assertEqual(d.replace(day=24), datetime.date(2025, 3, 24)) + self.assertEqual(d + datetime.timedelta(days=1), datetime.date(2025, 3, 24)) + self.assertEqual(d - datetime.timedelta(days=1), datetime.date(2025, 3, 22)) + self.assertEqual(d - datetime.date(2025, 3, 20), datetime.timedelta(days=3)) + self.assertEqual(d - LocalDate.of(2025, 3, 20), datetime.timedelta(days=3)) + + def test_foreign_time_behavior(self): + import datetime + import java + + LocalTime = java.type("java.time.LocalTime") + + t = LocalTime.of(7, 8, 9) + self.assertEqual(t.hour, 7) + self.assertEqual(t.minute, 8) + self.assertEqual(t.second, 9) + self.assertEqual(t.microsecond, 0) + self.assertEqual(str(t), "07:08:09") + self.assertEqual(t.isoformat(), "07:08:09") + self.assertEqual(t.strftime("%H:%M:%S"), "07:08:09") + self.assertEqual(format(t, "%H:%M:%S"), "07:08:09") + self.assertEqual(hash(t), hash(datetime.time(7, 8, 9))) + self.assertEqual(t, datetime.time(7, 8, 9)) + self.assertEqual(t, LocalTime.of(7, 8, 9)) + self.assertEqual(t.replace(second=10), datetime.time(7, 8, 10)) + self.assertLess(t, datetime.time(7, 8, 10)) + self.assertIsNone(t.tzinfo) + self.assertIsNone(t.utcoffset()) + self.assertIsNone(t.dst()) + self.assertIsNone(t.tzname()) + + def test_foreign_datetime_behavior(self): + import datetime + import java + + LocalDateTime = java.type("java.time.LocalDateTime") + ZonedDateTime = java.type("java.time.ZonedDateTime") + ZoneId = java.type("java.time.ZoneId") + + dt = LocalDateTime.of(2025, 3, 23, 7, 8, 9) + self.assertEqual(dt.year, 2025) + self.assertEqual(dt.month, 3) + self.assertEqual(dt.day, 23) + self.assertEqual(dt.hour, 7) + self.assertEqual(dt.minute, 8) + self.assertEqual(dt.second, 9) + self.assertEqual(dt.microsecond, 0) + self.assertEqual(str(dt), "2025-03-23 07:08:09") + self.assertEqual(dt.isoformat(), "2025-03-23T07:08:09") + self.assertEqual(dt.date(), datetime.date(2025, 3, 23)) + self.assertEqual(dt.time(), datetime.time(7, 8, 9)) + self.assertEqual(dt.timetz(), datetime.time(7, 8, 9)) + self.assertEqual(dt.timetuple(), datetime.datetime(2025, 3, 23, 7, 8, 9).timetuple()) + self.assertEqual(hash(dt), hash(datetime.datetime(2025, 3, 23, 7, 8, 9))) + self.assertEqual(dt, datetime.datetime(2025, 3, 23, 7, 8, 9)) + self.assertEqual(dt, LocalDateTime.of(2025, 3, 23, 7, 8, 9)) + self.assertEqual(dt.replace(minute=9), datetime.datetime(2025, 3, 23, 7, 9, 9)) + self.assertEqual(dt + datetime.timedelta(days=1), datetime.datetime(2025, 3, 24, 7, 8, 9)) + self.assertEqual(dt - datetime.timedelta(days=1), datetime.datetime(2025, 3, 22, 7, 8, 9)) + self.assertEqual(dt - datetime.datetime(2025, 3, 20, 7, 8, 9), datetime.timedelta(days=3)) + self.assertLess(dt, datetime.datetime(2025, 3, 23, 7, 8, 10)) + self.assertIsNone(dt.tzinfo) + self.assertIsNone(dt.utcoffset()) + self.assertIsNone(dt.dst()) + self.assertIsNone(dt.tzname()) + + berlin = ZoneId.of("Europe/Berlin") + zoned_dt = ZonedDateTime.of(2025, 3, 23, 7, 8, 9, 0, berlin) + self.assertIsInstance(zoned_dt.tzinfo, datetime.tzinfo) + self.assertEqual(zoned_dt.utcoffset(), datetime.timedelta(hours=1)) + self.assertEqual(zoned_dt.dst(), datetime.timedelta()) + self.assertEqual(zoned_dt.tzname(), "CET") + self.assertEqual(zoned_dt.isoformat(), "2025-03-23T07:08:09+01:00") + + def test_foreign_timezone_behavior(self): + import datetime + import java + + ZoneId = java.type("java.time.ZoneId") + ZonedDateTime = java.type("java.time.ZonedDateTime") + + utc = ZoneId.of("UTC") + self.assertIsInstance(utc, datetime.tzinfo) + self.assertEqual(str(utc), "UTC") + self.assertEqual(utc.tzname(None), "UTC") + self.assertEqual(utc.utcoffset(None), datetime.timedelta()) + self.assertIsNone(utc.dst(None)) + + aware = datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=utc) + self.assertIs(aware.tzinfo, utc) + self.assertEqual(aware.utcoffset(), datetime.timedelta()) + self.assertEqual(aware.tzname(), "UTC") + self.assertEqual(aware.isoformat(), "2025-03-23T07:08:09+00:00") + + berlin = ZoneId.of("Europe/Berlin") + self.assertIsInstance(berlin, datetime.tzinfo) + self.assertIsNone(berlin.utcoffset(None)) + self.assertIsNone(berlin.dst(None)) + self.assertIsNone(berlin.tzname(None)) + + local = datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=berlin) + self.assertIs(local.tzinfo, berlin) + self.assertEqual(local.utcoffset(), datetime.timedelta(hours=1)) + self.assertEqual(local.dst(), datetime.timedelta()) + self.assertEqual(local.tzname(), "CET") + self.assertEqual(berlin.fromutc(datetime.datetime(2025, 3, 23, 6, 8, 9, tzinfo=berlin)), + datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=berlin)) + + foreign_aware = ZonedDateTime.of(2025, 3, 23, 6, 8, 9, 0, berlin) + self.assertEqual(berlin.fromutc(foreign_aware), + datetime.datetime(2025, 3, 23, 7, 8, 9, tzinfo=berlin)) + + overlap = berlin.fromutc(datetime.datetime(2025, 10, 26, 1, 30, tzinfo=berlin)) + self.assertEqual(overlap, datetime.datetime(2025, 10, 26, 2, 30, tzinfo=berlin, fold=1)) + self.assertEqual(overlap.fold, 1) + self.assertEqual(overlap.utcoffset(), datetime.timedelta(hours=1)) + def test_read(self): o = CustomObject() assert polyglot.__read__(o, "field") == o.field @@ -347,14 +514,6 @@ def test_host_lookup(self): else: assert False, "requesting a non-existing host symbol should raise KeyError" - def test_internal_languages_dont_eval(self): - try: - polyglot.eval(language="nfi", string="default") - except ValueError as e: - assert str(e) == "polyglot language 'nfi' not found" - - assert polyglot.eval(language="python", string="21 * 2") == 42 - def test_module_eval_returns_last_expr(self): assert polyglot.eval(language="python", string="x = 2; x") == 2 @@ -675,7 +834,7 @@ class MyHandler (Handler): counter = 0; def isLoggable(self, logrecord): self.counter = self.counter + 1 - return self.__super__.isLoggable(logrecord) + return super().isLoggable(logrecord) def sayHello(self): return 'Hello' @@ -726,9 +885,9 @@ def test_extend_java_class_03(self): class MyLogRecord(LogRecord): def getLevel(self): - if self.__super__.getLevel() == Level.FINEST: - self.__super__.setLevel(Level.WARNING) - return self.__super__.getLevel() + if super().getLevel() == Level.FINEST: + super().setLevel(Level.WARNING) + return super().getLevel() message = "log message" my_lr1 = MyLogRecord(Level.WARNING, message) @@ -1503,6 +1662,18 @@ def getName(self): assert pl.callStaticFromPython("INFO").getName() == "INFO" assert PythonLevel2.parse("INFO").getName() == "INFO" + def test_java_subclassing_with_new_arguments(self): + from java.util.logging import Level + + class PythonLevel(Level, new_style=True): + def __new__(cls, misc_value): + return super().__new__(cls, "default name", 2) + + def __init__(self, misc_value): + self.misc_value = misc_value + + pl = PythonLevel(123) + assert pl.misc_value == 123 def test_jython_star_import(self): if __graalpython__.jython_emulation_enabled: diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_io.py b/graalpython/com.oracle.graal.python.test/src/tests/test_io.py index 0faeb0597c..f8108e209b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_io.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_io.py @@ -1,4 +1,4 @@ -# 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 @@ -38,12 +38,16 @@ # SOFTWARE. import _io import os +import subprocess import sys import tempfile import unittest class IOBaseTests(unittest.TestCase): + @staticmethod + def run_in_subprocess(code): + return subprocess.run([sys.executable, "-c", code], stdout=subprocess.PIPE, stderr=subprocess.PIPE) def test_iobase_ctor_accepts_anything(self): _io._IOBase() @@ -219,6 +223,68 @@ def test_exit_accepts_varargs(self): with self.assertRaises(TypeError): x.__exit__(kw=1) + def test_shutdown_stdout_lock_error_does_not_leak_to_stderr(self): + code = """import sys + +class StdoutAtShutdown: + closed = False + + def write(self, data): + return len(data) + + def flush(self): + raise SystemError( + "could not acquire lock for <_io.BufferedWriter name=''> " + "at interpreter shutdown, possibly due to daemon threads") + +sys.stdout = StdoutAtShutdown() +""" + proc = self.run_in_subprocess(code) + stderr = proc.stderr.decode("utf-8", "replace").replace("\r\n", "\n") + self.assertEqual(proc.stdout, b"") + self.assertEqual(proc.returncode, 120) + self.assertRegex( + stderr, + r"Exception ignored in: <__main__\.StdoutAtShutdown object at 0x[0-9a-fA-F]+>\n" + r"Traceback \(most recent call last\):\n" + r' File "", line 10, in flush\n' + r"SystemError: could not acquire lock for <_io\.BufferedWriter name=''> " + r"at interpreter shutdown, possibly due to daemon threads\n?$", + ) + + def test_shutdown_stdout_deadlock_matches_cpython(self): + code = """if 1: + import sys + import time + import threading + + file = sys.stdout + + def run(): + while True: + file.write('.') + file.flush() + + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() + + time.sleep(0.5) + file.write('!') + file.flush() +""" + proc = self.run_in_subprocess(code) + stderr = proc.stderr.decode("utf-8", "replace") + if proc.returncode != 0: + self.assertRegex( + stderr, + r"Fatal Python error: _enter_buffered_busy: could not acquire lock " + r"for <(_io\.)?BufferedWriter name=''> at interpreter shutdown, " + r"possibly due to daemon threads", + ) + else: + self.assertEqual(stderr, "") + def test_isatty(self): x = _io._IOBase() self.assertFalse(x.isatty()) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_itertools.py b/graalpython/com.oracle.graal.python.test/src/tests/test_itertools.py index edda296886..41323ee730 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_itertools.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_itertools.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2022, Oracle and/or its affiliates. +# Copyright (c) 2019, 2026, Oracle and/or its affiliates. # Copyright (C) 1996-2017 Python Software Foundation # # Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -104,7 +104,7 @@ def keyfunc(obj): def test_generators(self): # test that generators are accepted like input values - + def g(seqn): for i in seqn: yield i @@ -112,7 +112,7 @@ def g(seqn): def g2(seqn): for i in seqn: yield (i, i) - + self.assertEqual(list(accumulate(g([1,2]))), [1, 3]) self.assertEqual(list(chain(g([1,2]), g([3,4]))), [1, 2, 3, 4]) self.assertEqual(list(combinations(g([1,2]), 2)), [(1, 2)]) @@ -130,6 +130,25 @@ def g2(seqn): self.assertEqual(list(tee(g([1, 2]))[0]), [1, 2]) self.assertEqual(list(zip_longest(g2([2,3]))), [((2, 2),), ((3, 3),)]) + def test_product_repeat_index(self): + class Index: + def __init__(self, value): + self.value = value + + def __index__(self): + return self.value + + self.assertEqual(list(product([1, 2], repeat=Index(2))), [(1, 1), (1, 2), (2, 1), (2, 2)]) + self.assertEqual(list(product([1, 2], repeat=Index(0))), [()]) + self.assertRaises(ValueError, product, [1, 2], repeat=Index(-1)) + self.assertRaises(ValueError, product, [1, 2], repeat=-1) + self.assertRaises(TypeError, product, [1, 2], repeat=None) + self.assertRaises(TypeError, product, [1, 2], repeat=object()) + + def test_islice_negative_stop(self): + self.assertRaises(ValueError, islice, count(), -1) + self.assertRaises(ValueError, islice, count(), 0, -1) + @unittest.skipIf(sys.implementation.name == 'cpython' and sys.version_info[0:2] < (3, 10), "skipping for cPython versions < 3.10") def test_pairwise_drained(self): p = pairwise("abcd") diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_json.py b/graalpython/com.oracle.graal.python.test/src/tests/test_json.py index a074a5bb72..b94d05e36a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_json.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_json.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2024, 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 @@ -59,6 +59,26 @@ class JsonTest(unittest.TestCase): + def test_callable_object_hook(self): + called = [] + class Hook: + def __call__(self, obj): + called.append(True) + return obj + + hook_instance = Hook() + result = json.loads('{"a": 1, "b": 2}', object_hook=hook_instance) + assert len(called) == 1 + assert result == {"a": 1, "b": 2} + + def test_invalid_object_hook(self): + with self.assertRaises(TypeError): + json.loads('{"a": 1}', object_hook="not_a_function") + + def test_invalid_object_pairs_hook(self): + with self.assertRaises(TypeError): + json.loads('{"a": 1}', object_pairs_hook=12345) + def test_dump(self): cwd = os.getcwd() new_file_path = os.path.join(cwd, 'myFile.json') @@ -88,3 +108,11 @@ def test_encode_surrogate(self): assert s == '{"foo": "\\uda6a"}' s = json.dumps({'foo': "\uda6a"}, ensure_ascii=False) assert s == '{"foo": "\uda6a"}' + + def test_object_hook_nested(self): + def hook(obj): + return "hooked" + + assert json.loads('{"outer": {"inner": {"leaf": 1}}}', object_hook=hook) == "hooked" + assert json.loads('{"outer": {"inner": {"leaf": 1}}}', object_pairs_hook=hook) == "hooked" + 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_lzma.py b/graalpython/com.oracle.graal.python.test/src/tests/test_lzma.py new file mode 100644 index 0000000000..50a156f490 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_lzma.py @@ -0,0 +1,71 @@ +# 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. +import lzma +import unittest + +from tests.util import is_native_compression_backend + +DATA = (b"native lzma support migration test data\n" * 128) + bytes(range(256)) + + +@unittest.skipUnless(is_native_compression_backend(), "requires native compression backend") +def test_lzma_stream_roundtrip_and_unused_data(): + compressor = lzma.LZMACompressor(format=lzma.FORMAT_XZ, check=lzma.CHECK_CRC64) + compressed = compressor.compress(DATA[:113]) + compressed += compressor.compress(DATA[113:]) + compressed += compressor.flush() + + assert lzma.decompress(compressed) == DATA + + decompressor = lzma.LZMADecompressor() + assert decompressor.decompress(compressed + b"tail") == DATA + assert decompressor.eof + assert decompressor.unused_data == b"tail" + + +@unittest.skipUnless(is_native_compression_backend(), "requires native compression backend") +def test_lzma_raw_filter_roundtrip(): + filters = [{"id": lzma.FILTER_LZMA2, "preset": 1}] + compressor = lzma.LZMACompressor(format=lzma.FORMAT_RAW, filters=filters) + compressed = compressor.compress(DATA) + compressed += compressor.flush() + + decompressor = lzma.LZMADecompressor(format=lzma.FORMAT_RAW, filters=filters) + assert decompressor.decompress(compressed) == DATA + assert decompressor.eof diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_mappingproxy.py b/graalpython/com.oracle.graal.python.test/src/tests/test_mappingproxy.py index b14411f4c5..8d55228486 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_mappingproxy.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_mappingproxy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2022, 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,6 +82,14 @@ def test_views(): assert set(mp.items()) == {('a', 1), ('b', 2), ('c', 3)}, "items view invalid" +def test_values_descriptor_rejects_non_mappingproxy(): + class NotADict: + values = type(object.__dict__).values + + assert_raises(TypeError, type(object.__dict__).values.__get__, NotADict(), NotADict) + assert_raises(TypeError, lambda: NotADict().values) + + def test_init(): class CustomMappingObject: def __init__(self, keys, values): @@ -130,8 +138,8 @@ def test_iter(): mp_keys = set([k for k in mp]) assert d.keys() == mp_keys - -def test_create(): + +def test_create(): _mappingproxy(dict()) mp = _mappingproxy({'a': 1}) _mappingproxy(mp) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_math.py b/graalpython/com.oracle.graal.python.test/src/tests/test_math.py index 3026992f16..b4cd1564af 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_math.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_math.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, 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 @@ -214,7 +214,7 @@ def testAcos(self): class MyFloat2: def __float__(self): return 1.6 - self.assertRaises(ValueError, math.acos, MyFloat2()) + self.assertRaises(ValueError, math.acos, MyFloat2()) class MyFloat3: def __float__(self): @@ -265,7 +265,7 @@ def testSqrt(self): self.assertRaises(ValueError, math.sqrt, -1) self.assertRaises(ValueError, math.sqrt, NINF) self.assertTrue(math.isnan(math.sqrt(NAN))) - + math.sqrt(MyFloat()) math.sqrt(BIG_INT) self.assertRaises(TypeError, math.asin, 'ahoj') @@ -307,7 +307,7 @@ def testLog1p(self): self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) self.assertRaises(ValueError, math.log1p, -1) self.assertEqual(math.log1p(INF), INF) - + # test of specializations self.ftest('log1p(MyFloat())', math.log1p(MyFloat()), 0.4700036292457356) self.assertRaises(TypeError, math.log1p, 'ahoj') @@ -382,7 +382,7 @@ def testIsinf(self): self.assertFalse(math.isinf(float("nan"))) self.assertFalse(math.isinf(0.)) self.assertFalse(math.isinf(1.)) - + self.assertFalse(math.isinf(True)) self.assertFalse(math.isinf(LONG_INT)) self.assertFalse(math.isinf(BIG_INT)) @@ -419,6 +419,7 @@ def __ceil__(self): return 'hello' self.assertEqual(math.ceil(I(22)), 22) + self.assertEqual(type(math.ceil(I(22))), int) self.assertEqual(math.ceil(I2(256)), 11) self.assertEqual(math.ceil(I(156)), 156) self.assertEqual(math.ceil(I2(777)), 11) @@ -484,7 +485,7 @@ def test_basic_copysign(self): self.assertEqual(math.copysign(999999999999999999999.1, 1), 999999999999999999999.1) self.assertRaises(TypeError, math.copysign, 'hello', 1) self.assertRaises(TypeError, math.copysign, 1, 'hello') - + self.assertEqual(math.copysign(MyFloat(), 1), 0.6) self.assertEqual(math.copysign(MyFloat(), -1), -0.6) self.assertEqual(math.copysign(1.2, MyFloat()), 1.2) @@ -679,12 +680,12 @@ def testPow(self): self.assertEqual(math.pow(999999999999999999999999999, 0), 1) self.assertEqual(math.pow(0.0, 999999999999999999999999999), 0) self.assertEqual(math.pow(999999999999999999999999999, 0.0), 1) - + class MyNumber(): def __float__(self): return -2.; self.ftest('MyFloat()**-3.', math.pow(MyNumber(), -3.0), -0.125) - + def testAtan2(self): self.assertRaises(TypeError, math.atan2) self.ftest('atan2(-1, 0)', math.atan2(-1, 0), -math.pi/2) @@ -768,7 +769,7 @@ def testCos(self): self.assertRaises(ValueError, math.cos, INF) self.assertRaises(ValueError, math.cos, NINF) self.assertTrue(math.isnan(math.cos(NAN))) - + #test of specializations self.ftest('cos(BIG_INT)', math.cos(BIG_INT), 0.4145587418469303) self.ftest('cos(MyFloat())', math.cos(MyFloat()), 0.8253356149096783) @@ -786,7 +787,7 @@ def testCosh(self): self.ftest('cosh(MyFloat())', math.cosh(MyFloat()), 1.1854652182422676) self.assertRaises(TypeError, math.cosh, 'ahoj') self.assertRaises(OverflowError, math.cosh, BIG_INT) - + def testSin(self): self.assertRaises(TypeError, math.sin) self.ftest('sin(0)', math.sin(0), 0) @@ -813,7 +814,7 @@ def testSinh(self): self.assertEqual(math.sinh(INF), INF) self.assertEqual(math.sinh(NINF), NINF) self.assertTrue(math.isnan(math.sinh(NAN))) - + # test of specializations self.ftest('sinh(MyFloat())', math.sinh(MyFloat()), 0.6366535821482412) self.assertRaises(TypeError, math.sinh, 'ahoj') @@ -1128,7 +1129,7 @@ def testExp(self): self.assertEqual(math.exp(NINF), 0.) self.assertTrue(math.isnan(math.exp(NAN))) self.assertRaises(OverflowError, math.exp, 1000000) - + # test of specializations self.ftest('exp(MyFloat())', math.exp(MyFloat()), 1.8221188003905089) self.assertRaises(TypeError, math.exp, 'ahoj') @@ -1239,7 +1240,7 @@ class II(int): self.assertRaises(TypeError, math.ldexp, 'Hello', 1000000) self.assertRaises(TypeError, math.ldexp, 1, 'Hello') self.assertEqual(math.ldexp(7589167167882033, -48), 26.962138008038156) - + self.assertRaises(TypeError, math.ldexp, 1, MyIndexable(2)) self.assertRaises(TypeError, math.ldexp, 1, MyInt(2)) self.assertRaises(TypeError, math.ldexp, 1, MyFloat()) @@ -1272,6 +1273,20 @@ class TestNoTrunc(object): self.assertRaises(TypeError, math.trunc, 1, 2) self.assertRaises(TypeError, math.trunc, TestNoTrunc()) + class MissingTrunc: + def __getattr__(self, name): + if name == "__trunc__": + return lambda: 99 + raise AttributeError(name) + + self.assertRaises(TypeError, math.trunc, MissingTrunc()) + + class NotImplementedTrunc: + def __trunc__(self): + return NotImplemented + + self.assertIs(math.trunc(NotImplementedTrunc()), NotImplemented) + def testDegrees(self): self.assertRaises(TypeError, math.degrees) self.ftest('degrees(pi)', math.degrees(math.pi), 180.0) @@ -1315,6 +1330,11 @@ def testmodf(name, result, expected): self.assertTrue(math.isnan(modf_nan[0])) self.assertTrue(math.isnan(modf_nan[1])) + small_negative_fraction, small_negative_integral = math.modf(-4.0755017301539587e-25) + self.assertEqual(small_negative_fraction, -4.0755017301539587e-25) + self.assertEqual(math.copysign(1.0, small_negative_integral), -1.0) + self.assertEqual(small_negative_integral, -0.0) + # test of specializations testmodf('modf(MyFloat())', math.modf(MyFloat()), (0.6, 0.0)) self.assertRaises(TypeError, math.modf, 'ahoj') @@ -1325,7 +1345,7 @@ def executeFnTest(self, values, fn, fnName): result = fn(value[0]) expected = value[1] if math.isnan(expected): - self.assertTrue(math.isnan(result), "Test2 fail: {}({}) = {}, but was {}".format(fnName, value[0], expected, result)) + self.assertTrue(math.isnan(result), "Test2 fail: {}({}) = {}, but was {}".format(fnName, value[0], expected, result)) else : if result != expected: if (sys.version_info.major >= 3 and sys.version_info.minor >= 5): @@ -1334,7 +1354,7 @@ def executeFnTest(self, values, fn, fnName): def test_erf(self): erfValues = [(0.0, 0.0), (-0.0, -0.0), (INF, 1.0), (NINF, -1.0), (NAN, NAN), # tiny values - (1e-308, 1.1283791670955125e-308), (5e-324, 4.9406564584124654e-324), + (1e-308, 1.1283791670955125e-308), (5e-324, 4.9406564584124654e-324), (1e-10, 1.1283791670955126e-10), # small integers (1, 0.842700792949715), (2, 0.99532226501895271), (3, 0.99997790950300136), @@ -1356,7 +1376,7 @@ def test_erfc(self): (1e-308, 1.0), (5e-324, 1.0), (1e-10, 0.99999999988716204), # small integers (1, 0.157299207050285), (2, 0.004677734981047268), (3, 2.2090496998585482e-05), - (4, 1.541725790028002e-08), (5, 1.5374597944280341e-12), + (4, 1.541725790028002e-08), (5, 1.5374597944280341e-12), # this number needs to be rounded (6, 2.1519736712498925e-17), (-1, 1.842700792949715), (-2, 1.9953222650189528), (-3, 1.9999779095030015), @@ -1372,7 +1392,7 @@ def test_erfc(self): (-26.8, 2.0), (-27.0, 2.0), (-27.2, 2.0), (-27.4, 2.0), (-27.6, 2.0) ] self.executeFnTest(values, math.erfc, 'math.erfc') - + def test_gamma(self): self.assertRaises(ValueError, math.gamma, 0.) self.assertRaises(ValueError, math.gamma, -0.0) @@ -1400,34 +1420,34 @@ def test_gamma(self): (3.5, 3.323350970447842), (-0.5, -3.5449077018110322), (-1.5, 2.3632718012073544), (-2.5, -0.94530872048294170), (-3.5, 0.27008820585226917), # values near 0 - (0.1, 9.5135076986687306), - (0.01, 99.432585119150602), + (0.1, 9.5135076986687306), + (0.01, 99.432585119150602), (1e-8, 99999999.422784343), - #(1e-16, 10000000000000000), + #(1e-16, 10000000000000000), (1e-30, 9.9999999999999988e+29), (1e-160, 1.0000000000000000e+160), - (1e-308, 1.0000000000000000e+308), + (1e-308, 1.0000000000000000e+308), (5.6e-309, 1.7857142857142848e+308), - (-0.1, -10.686287021193193), - (-0.01, -100.58719796441078), + (-0.1, -10.686287021193193), + (-0.01, -100.58719796441078), (-1e-8, -100000000.57721567), - (-1e-16, -10000000000000000), + (-1e-16, -10000000000000000), (-1e-30, -9.9999999999999988e+29), (-1e-160, -1.0000000000000000e+160), - (-1e-308, -1.0000000000000000e+308), + (-1e-308, -1.0000000000000000e+308), (-5.6e-309, -1.7857142857142848e+308), # values near negative integers - (-0.99999999999999989, -9007199254740992.0), + (-0.99999999999999989, -9007199254740992.0), (-1.0000000000000002, 4503599627370495.5), - (-1.9999999999999998, 2251799813685248.5), + (-1.9999999999999998, 2251799813685248.5), (-2.0000000000000004, -1125899906842623.5), - (-100.00000000000001, -7.5400833348831090e-145), + (-100.00000000000001, -7.5400833348831090e-145), (-99.999999999999986, 7.5400833348840962e-145), # large inputs - (170, 4.2690680090047051e+304), - (171, 7.2574156153079990e+306), + (170, 4.2690680090047051e+304), + (171, 7.2574156153079990e+306), (171.624, 1.7942117599248104e+308), # inputs for which gamma(x) is tiny - (-100.5, -3.3536908198076787e-159), - (-160.5, -5.2555464470078293e-286), + (-100.5, -3.3536908198076787e-159), + (-160.5, -5.2555464470078293e-286), (-170.5, -3.3127395215386074e-308), (-171.5, 1.9316265431711902e-310), (-176.5, -1.1956388629358166e-321), (-177.5, 4.9406564584124654e-324), (-178.5, -0.0), (-179.5, 0.0), (-201.0001, 0.0), (-202.9999, -0.0), (-1000.5, -0.0), @@ -1452,11 +1472,11 @@ def test_lgamma(self): values = [(INF, INF), (-INF, INF), (NAN, NAN), # small positive integers give factorials - (1, 0.0), (2, 0.0), - (3, 0.69314718055994529), - (4, 1.791759469228055), - (5, 3.1780538303479458), - (6, 4.7874917427820458), + (1, 0.0), (2, 0.0), + (3, 0.69314718055994529), + (4, 1.791759469228055), + (5, 3.1780538303479458), + (6, 4.7874917427820458), # half integers (0.5, 0.57236494292470008), (1.5, -0.12078223763524522), diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_memoryview.py b/graalpython/com.oracle.graal.python.test/src/tests/test_memoryview.py index 3621dfde2e..1df2c2faab 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_memoryview.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_memoryview.py @@ -1,9 +1,10 @@ -# Copyright (c) 2018, 2024, 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 import sys +import struct import unittest from tests.util import assert_raises @@ -93,6 +94,7 @@ def test_slice(): assert e1 == e2 def test_unpack(): + long_bytes = b'\xaa' * struct.calcsize('l') assert memoryview(b'\xaa')[0] == 170 assert memoryview(b'\xaa').cast('B')[0] == 170 assert memoryview(b'\xaa').cast('b')[0] == -86 @@ -100,8 +102,8 @@ def test_unpack(): assert memoryview(b'\xaa\xaa').cast('h')[0] == -21846 assert memoryview(b'\xaa\xaa\xaa\xaa').cast('I')[0] == 2863311530 assert memoryview(b'\xaa\xaa\xaa\xaa').cast('i')[0] == -1431655766 - assert memoryview(b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa').cast('L')[0] == 12297829382473034410 - assert memoryview(b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa').cast('l')[0] == -6148914691236517206 + assert memoryview(long_bytes).cast('L')[0] == struct.unpack('L', long_bytes)[0] + assert memoryview(long_bytes).cast('l')[0] == struct.unpack('l', long_bytes)[0] assert memoryview(b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa').cast('Q')[0] == 12297829382473034410 assert memoryview(b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa').cast('q')[0] == -6148914691236517206 assert memoryview(b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa').cast('N')[0] == 12297829382473034410 @@ -114,6 +116,7 @@ def test_unpack(): assert memoryview(b'\xaa').cast('c')[0] == b'\xaa' def test_pack(): + long_bytes = b'\xaa' * struct.calcsize('l') b = bytearray(1) memoryview(b).cast('B')[0] = 170 assert b == b'\xaa' @@ -132,12 +135,12 @@ def test_pack(): b = bytearray(4) memoryview(b).cast('i')[0] = -1431655766 assert b == b'\xaa\xaa\xaa\xaa' - b = bytearray(8) - memoryview(b).cast('L')[0] = 12297829382473034410 - assert b == b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' - b = bytearray(8) - memoryview(b).cast('l')[0] = -6148914691236517206 - assert b == b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' + b = bytearray(struct.calcsize('L')) + memoryview(b).cast('L')[0] = struct.unpack('L', long_bytes)[0] + assert b == long_bytes + b = bytearray(struct.calcsize('l')) + memoryview(b).cast('l')[0] = struct.unpack('l', long_bytes)[0] + assert b == long_bytes b = bytearray(8) memoryview(b).cast('Q')[0] = 12297829382473034410 assert b == b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa' diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_methods.py b/graalpython/com.oracle.graal.python.test/src/tests/test_methods.py index 7cda8a38f0..0f518fbadf 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_methods.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_methods.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2021, 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 @@ -89,3 +89,53 @@ def __call__(self, x, y): assert isinstance(method1(), A) assert method2(1) == "A is 1", method2(1) + + +def test_method_qualname_uses_wrapped_callable(): + import types + + def f(): + pass + + class A: + def g(self): + pass + + m = classmethod(f) + + assert A().g.__qualname__ == "test_method_qualname_uses_wrapped_callable..A.g" + assert types.MethodType(f, A).__qualname__ == "test_method_qualname_uses_wrapped_callable..f" + assert A.m.__qualname__ == "test_method_qualname_uses_wrapped_callable..f" + assert A.__dict__["m"].__qualname__ == "test_method_qualname_uses_wrapped_callable..f" + + +def test_method_getattribute_does_not_swallow_method_descriptor_attribute_error(): + import types + + class Callable: + def __init__(self): + self.name_calls = 0 + + def __call__(self, *args): + pass + + def __getattribute__(self, name): + if name == "__name__": + calls = object.__getattribute__(self, "name_calls") + object.__setattr__(self, "name_calls", calls + 1) + if calls == 0: + raise AttributeError("first lookup failure") + return "fallback-name" + return object.__getattribute__(self, name) + + func = Callable() + method = types.MethodType(func, object()) + try: + method.__name__ + except AttributeError as e: + assert str(e) == "first lookup failure" + else: + assert False, "AttributeError was not raised" + assert func.name_calls == 1 + assert method.__class__ is types.MethodType + assert method.__name__ == "fallback-name" diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_mmap.py b/graalpython/com.oracle.graal.python.test/src/tests/test_mmap.py index 41f9d78738..082966834a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_mmap.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_mmap.py @@ -1,4 +1,4 @@ -# Copyright (c) 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 @@ -38,11 +38,25 @@ # SOFTWARE. import mmap +import sys PAGESIZE = mmap.PAGESIZE FIND_BUFFER_SIZE = 1024 # keep in sync with FindNode#BUFFER_SIZE +def test_map_private_constant_matches_platform(): + assert hasattr(mmap, "MAP_PRIVATE") == (sys.platform != "win32") + + +def test_access_copy_without_map_private_constant(): + m = mmap.mmap(-1, 1, access=mmap.ACCESS_COPY) + try: + m[0] = b'a'[0] + assert m[:] == b'a' + finally: + m.close() + + def test_find(): cases = [ # (size, needle_pos) @@ -103,4 +117,4 @@ def test_iter(): for i in m: l.append(i) - assert l == [b'\x02', b'\x03', b'\x04'] \ No newline at end of file + assert l == [b'\x02', b'\x03', b'\x04'] 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 0a49071bb5..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 @@ -1,4 +1,4 @@ -# Copyright (c) 2020, 2024, 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,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() @@ -80,11 +83,14 @@ def test_wait_timeout(): res = wait(fds, timeout) delta = time.monotonic() - start assert not res - assert delta < timeout * 2 + # The GraalPy multiprocessing wait path actively polls fake file descriptors and may + # overshoot under scheduling contention. + assert delta < timeout * 8 assert delta > timeout / 2 @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_object.py b/graalpython/com.oracle.graal.python.test/src/tests/test_object.py index 005b600e7e..427908ad5e 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_object.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_object.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2023, 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 @@ -183,6 +183,11 @@ def test_descr_call_with_none(): assert None.__bool__() is False assert_raises(TypeError, descr.__get__, None, None) + +def test_builtin_descriptors_reject_invalid_receivers(): + assert_raises(TypeError, dict.get.__get__, 1, int) + assert_raises(TypeError, str.__str__.__get__, 1, int) + def test_custom_getattribute(): class AAA: __slots__ = '__wrapped__' @@ -210,14 +215,14 @@ def __getattr__(self, name): raise ValueError('wrapper has not been initialised') return getattr(self.__wrapped__, name) - + def __iter__(self): return iter(self.__wrapped__) class BBB(AAA): def __init__(self, wrapped_dict=None): AAA.__init__(self, wrapped_dict) - + def __getattribute__(self, name): if (hasattr(type(self), name) and isinstance(getattr(type(self), name), property)): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_parser.py b/graalpython/com.oracle.graal.python.test/src/tests/test_parser.py index 49edd17402..ddd96cc260 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_parser.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_parser.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 @@ -779,7 +779,6 @@ def test_annotations_in_function(): exec(code,test_globals) assert len(test_globals['__annotations__']) == 0 assert len(test_globals['fn'].__annotations__) == 0 - assert 1 not in test_globals['fn'].__code__.co_consts # the annotation is ignored in function source = '''def fn(): a:int =1 @@ -789,7 +788,6 @@ def test_annotations_in_function(): assert len(test_globals['__annotations__']) == 0 assert hasattr(test_globals['fn'], '__annotations__') assert len(test_globals['fn'].__annotations__) == 0 - assert 1 in test_globals['fn'].__code__.co_consts def test_annotations_in_class(): @@ -849,58 +847,6 @@ def test_annotations_in_class(): assert test_globals['Style'].__annotations__['_path'] == str assert '_path' in dir(test_globals['Style']) -def test_negative_float(): - - def check_const(fn, expected): - for const in fn.__code__.co_consts: - if repr(const) == repr(expected): - return True - else: - return False - - def fn1(): - return -0.0 - - assert check_const(fn1, -0.0) - - -def find_count_in(collection, what): - count = 0; - for item in collection: - if item == what: - count +=1 - return count - -def test_same_consts(): - def fn1(): a = 1; b = 1; return a + b - assert find_count_in(fn1.__code__.co_consts, 1) == 1 - - def fn2(): a = 'a'; b = 'a'; return a + b - assert find_count_in(fn2.__code__.co_consts, 'a') == 1 - -def test_tuple_in_const(): - def fn1() : return (0,) - assert (0,) in fn1.__code__.co_consts - assert 0 not in fn1.__code__.co_consts - - def fn2() : return (1, 2, 3, 1, 2, 3) - assert (1, 2, 3, 1, 2, 3) in fn2.__code__.co_consts - assert 1 not in fn2.__code__.co_consts - assert 2 not in fn2.__code__.co_consts - assert 3 not in fn2.__code__.co_consts - assert find_count_in(fn2.__code__.co_consts, (1, 2, 3, 1, 2, 3)) == 1 - - def fn3() : a = 1; return (1, 2, 1) - assert (1, 2, 1) in fn3.__code__.co_consts - assert find_count_in(fn3.__code__.co_consts, 1) == 1 - assert 2 not in fn3.__code__.co_consts - - def fn4() : a = 1; b = (1,2,3); c = 4; return (1, 2, 3, 1, 2, 3) - assert (1, 2, 3) in fn4.__code__.co_consts - assert (1, 2, 3, 1, 2, 3) in fn4.__code__.co_consts - assert 2 not in fn4.__code__.co_consts - assert find_count_in(fn4.__code__.co_consts, 1) == 1 - assert find_count_in(fn4.__code__.co_consts, 4) == 1 def test_ComprehensionGeneratorExpr(): def create_list(gen): diff --git a/graalpython/lib-python/3/test/test_patched_pip.py b/graalpython/com.oracle.graal.python.test/src/tests/test_patched_pip.py similarity index 87% rename from graalpython/lib-python/3/test/test_patched_pip.py rename to graalpython/com.oracle.graal.python.test/src/tests/test_patched_pip.py index c7716f7117..dc3647c4aa 100644 --- a/graalpython/lib-python/3/test/test_patched_pip.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_patched_pip.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 @@ -45,6 +45,7 @@ import tempfile import threading import unittest +import zipfile from http.server import HTTPServer, SimpleHTTPRequestHandler from pathlib import Path from urllib.parse import urljoin @@ -153,7 +154,12 @@ def build_package(self, name, version): def add_package_to_index(self, name, version, dist_type): package = self.build_package(name, version)[dist_type] - shutil.copy(package, self.index_dir) + # extra careful, not using shutil.copy so we get more detailed traceback + # on some flaky windows CI workers + self.assertTrue(package.exists(), f"Built package disappeared: {package}") + destination = self.index_dir / package.name + with open(package, 'rb') as src, open(destination, 'wb') as dst: + shutil.copyfileobj(src, dst) def run_venv_pip_install(self, package, extra_env=None, assert_stderr_matches=None): env = self.pip_env.copy() @@ -184,6 +190,34 @@ def run_venv_pip_install(self, package, extra_env=None, assert_stderr_matches=No f"Didn't match expected stderr.\nExpected (regex): {assert_stderr_matches}\nActual:{proc.stderr}" return re.findall(r'Successfully installed (\S+)', proc.stdout) + def run_venv_pip_runner_install(self, package): + runner = subprocess.check_output([ + self.venv_python, + '-c', + 'import pathlib, pip; print(pathlib.Path(pip.__file__).with_name("__pip-runner__.py"))', + ], universal_newlines=True).strip() + proc = subprocess.run( + [ + str(self.venv_python), + runner, + '--isolated', + 'install', + '--force-reinstall', + '--find-links', str(self.index_dir), + '--no-index', + '--no-cache-dir', + package, + ], + check=True, + capture_output=True, + env=self.pip_env, + universal_newlines=True, + ) + print(proc.stdout) + print(proc.stderr) + assert 'Applying GraalPy patch failed for' not in proc.stderr + return re.findall(r'Successfully installed (\S+)', proc.stdout) + def run_test_fun(self): code = "import patched_package; print(patched_package.test_fun())" return subprocess.check_output([self.venv_python, '-c', code], universal_newlines=True).strip() @@ -228,6 +262,35 @@ def test_sdist_patched_version(self): self.run_venv_pip_install('foo') assert self.run_test_fun() == "Patched" + def test_sdist_patched_version_with_pip_runner(self): + self.add_package_to_index('foo', '1.1.0', 'sdist') + self.prepare_config('foo', [{ + 'patch': 'foo-1.1.0.patch', + 'version': '== 1.1.0', + 'subdir': 'src', + }]) + self.run_venv_pip_runner_install('foo') + assert self.run_test_fun() == "Patched" + + def test_mark_wheel_preserves_executable_scripts(self): + import graalpy_pip_extensions + + wheel = self.build_dir / 'executable_script-1.0-py3-none-any.whl' + script = 'executable_script-1.0.data/scripts/executable-script' + with zipfile.ZipFile(wheel, 'w') as z: + info = zipfile.ZipInfo(script) + info.external_attr = 0o100755 << 16 + z.writestr(info, '#!/usr/bin/env python\n') + z.writestr('executable_script-1.0.dist-info/METADATA', 'Name: executable_script\nVersion: 1.0\n') + z.writestr('executable_script-1.0.dist-info/WHEEL', 'Wheel-Version: 1.0\n') + z.writestr('executable_script-1.0.dist-info/RECORD', '') + + graalpy_pip_extensions.mark_wheel(wheel) + + with zipfile.ZipFile(wheel) as z: + mode = z.getinfo(script).external_attr >> 16 + assert mode & 0o111 + def test_different_patch_wheel_sdist1(self): self.add_package_to_index('foo', '1.1.0', 'sdist') self.prepare_config('foo', [ diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_pdb.py b/graalpython/com.oracle.graal.python.test/src/tests/test_pdb.py index 3028614d89..2eb4fa7a5d 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_pdb.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_pdb.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 @@ -100,6 +100,86 @@ def doctest_pdb_locals(): """ +# Extracted from CPython test_pdb_basic_commands and test_pdb_issue_gh_91742. +# The full upstream doctests require unsupported Bytecode DSL f_lineno jumps; +# these keep the non-jump debugger coverage that is still relevant. +def doctest_pdb_args_for_kwonly_and_posonly(): + """ + Test that args displays keyword-only and positional-only parameters. + + >>> def kwonly_func(arg=None, *, kwonly=None): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... pass + + >>> def posonly_func(a, b, /, c=None): + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... pass + + >>> with PdbTestInput([ + ... 'args', + ... 'continue', + ... 'args', + ... 'continue', + ... ]): + ... kwonly_func('value', kwonly=True) + ... posonly_func(1, 2, c=3) + > (3)kwonly_func() + -> pass + (Pdb) args + arg = 'value' + kwonly = True + (Pdb) continue + > (3)posonly_func() + -> pass + (Pdb) args + a = 1 + b = 2 + c = 3 + (Pdb) continue + """ + + +def doctest_pdb_multiline_call_line_tracing(): + """ + Test stepping through a nested function and stopping on a multiline call continuation. + + >>> def test_function(): + ... __author__ = "pi" + ... __version__ = "3.14" + ... + ... def about(): + ... '''About''' + ... print(f"Author: {__author__!r}", + ... f"Version: {__version__!r}", + ... sep=" ") + ... + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() + ... about() + + >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'step', + ... 'next', + ... 'next', + ... 'continue', + ... ]): + ... test_function() + > (12)test_function() + -> about() + (Pdb) step + --Call-- + > (5)about() + -> def about(): + (Pdb) next + > (7)about() + -> print(f"Author: {__author__!r}", + (Pdb) next + > (8)about() + -> f"Version: {__version__!r}", + (Pdb) continue + Author: 'pi' Version: '3.14' + """ + + if not util.IS_BYTECODE_DSL: def doctest_pdb_locals_generator(): """ diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_pip_audit_hook.py b/graalpython/com.oracle.graal.python.test/src/tests/test_pip_audit_hook.py new file mode 100644 index 0000000000..8a18a4dc3f --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_pip_audit_hook.py @@ -0,0 +1,526 @@ +# 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 os +import shutil +import subprocess +import sys +import tempfile +import unittest +import zipfile +from pathlib import Path + + +def write_fake_pip(root, patched=False): + pip_package = root / "pip" + pip_package.mkdir() + pip_package.joinpath("__init__.py").write_text( + "__GRAALPY_PATCHED = True\n" if patched else "", encoding="utf-8" + ) + return root + + +class PipAuditHookTests(unittest.TestCase): + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + @unittest.skipUnless(shutil.which("patch"), "requires the patch utility") + def test_source_ready_event_applies_local_graalpy_patch_after_unpatched_pip_import(self): + with tempfile.TemporaryDirectory(prefix="graalpy-pip-audit-") as tmp: + root = Path(tmp) + patches = root / "patches" + source = root / "demo-1.0.0" + fake_pip_root = root / "fake-pip" + patches.mkdir() + source.mkdir() + fake_pip_root.mkdir() + write_fake_pip(fake_pip_root) + + (patches / "metadata.toml").write_text( + "\n".join( + [ + "[[demo.rules]]", + "version = '== 1.0.0'", + "patch = 'demo.patch'", + "license = 'MIT'", + ] + ), + encoding="utf-8", + ) + (patches / "demo.patch").write_text( + "\n".join( + [ + "diff --git a/demo_module.py b/demo_module.py", + "--- a/demo_module.py", + "+++ b/demo_module.py", + "@@ -1 +1 @@", + '-value = "old"', + '+value = "patched"', + "", + ] + ), + encoding="utf-8", + ) + module = source / "demo_module.py" + module.write_text('value = "old"\n', encoding="utf-8") + + env = os.environ.copy() + env["PIP_GRAALPY_PATCHES_URL"] = patches.as_uri() + env.pop("PIP_GRAALPY_DISABLE_PATCHING", None) + + subprocess.check_call( + [ + sys.executable, + "-S", + "-c", + ( + "import sys; " + "sys.path.insert(0, sys.argv[1]); " + "import pip; " + "sys.audit('pip.requirement.source_ready', " + "'demo', None, 'https://example.invalid/demo-1.0.0.tar.gz', " + "sys.argv[2], False, 0)" + ), + str(fake_pip_root), + str(source), + ], + env=env, + ) + + self.assertEqual( + module.read_text(encoding="utf-8"), 'value = "patched"\n' + ) + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + @unittest.skipUnless(shutil.which("patch"), "requires the patch utility") + def test_wheel_install_event_rewrites_wheel_before_install(self): + with tempfile.TemporaryDirectory(prefix="graalpy-pip-audit-wheel-") as tmp: + root = Path(tmp) + patches = root / "patches" + fake_pip_root = root / "fake-pip" + patches.mkdir() + fake_pip_root.mkdir() + write_fake_pip(fake_pip_root) + wheel_path = root / "demo-1.0.0-py3-none-any.whl" + + (patches / "metadata.toml").write_text( + "\n".join( + [ + "[[demo.rules]]", + "version = '== 1.0.0'", + "dist-type = 'wheel'", + "patch = 'demo.patch'", + "license = 'MIT'", + ] + ), + encoding="utf-8", + ) + (patches / "demo.patch").write_text( + "\n".join( + [ + "diff --git a/demo_module.py b/demo_module.py", + "--- a/demo_module.py", + "+++ b/demo_module.py", + "@@ -1 +1 @@", + '-value = "old"', + '+value = "patched"', + "", + ] + ), + encoding="utf-8", + ) + + with zipfile.ZipFile(wheel_path, "w") as z: + z.writestr("demo_module.py", 'value = "old"\n') + z.writestr( + "demo-1.0.0.dist-info/WHEEL", + "Wheel-Version: 1.0\nRoot-Is-Purelib: true\nTag: py3-none-any\n", + ) + z.writestr( + "demo-1.0.0.dist-info/METADATA", + "Metadata-Version: 2.1\nName: demo\nVersion: 1.0.0\n", + ) + z.writestr("demo-1.0.0.dist-info/RECORD", "") + + env = os.environ.copy() + env["PIP_GRAALPY_PATCHES_URL"] = patches.as_uri() + env.pop("PIP_GRAALPY_DISABLE_PATCHING", None) + + subprocess.check_call( + [ + sys.executable, + "-S", + "-c", + ( + "import sys; " + "sys.path.insert(0, sys.argv[1]); " + "import pip; " + "sys.audit('pip.wheel.install', 'demo', sys.argv[2])" + ), + str(fake_pip_root), + str(wheel_path), + ], + env=env, + ) + + with zipfile.ZipFile(wheel_path) as z: + self.assertEqual( + z.read("demo_module.py").decode("utf-8"), 'value = "patched"\n' + ) + self.assertIn("demo-1.0.0.dist-info/GRAALPY_MARKER", z.namelist()) + record = z.read("demo-1.0.0.dist-info/RECORD").decode("utf-8") + self.assertIn("demo_module.py,sha256=", record) + self.assertIn("demo-1.0.0.dist-info/GRAALPY_MARKER,sha256=", record) + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + @unittest.skipUnless(shutil.which("patch"), "requires the patch utility") + def test_patched_pip_does_not_install_audit_hook(self): + with tempfile.TemporaryDirectory(prefix="graalpy-pip-audit-patched-") as tmp: + root = Path(tmp) + patches = root / "patches" + source = root / "demo-1.0.0" + fake_pip_root = root / "fake-pip" + patches.mkdir() + source.mkdir() + fake_pip_root.mkdir() + write_fake_pip(fake_pip_root, patched=True) + + (patches / "metadata.toml").write_text( + "\n".join( + [ + "[[demo.rules]]", + "version = '== 1.0.0'", + "patch = 'demo.patch'", + "license = 'MIT'", + ] + ), + encoding="utf-8", + ) + (patches / "demo.patch").write_text( + "\n".join( + [ + "diff --git a/demo_module.py b/demo_module.py", + "--- a/demo_module.py", + "+++ b/demo_module.py", + "@@ -1 +1 @@", + '-value = "old"', + '+value = "patched"', + "", + ] + ), + encoding="utf-8", + ) + module = source / "demo_module.py" + module.write_text('value = "old"\n', encoding="utf-8") + + env = os.environ.copy() + env["PIP_GRAALPY_PATCHES_URL"] = patches.as_uri() + env.pop("PIP_GRAALPY_DISABLE_PATCHING", None) + + subprocess.check_call( + [ + sys.executable, + "-S", + "-c", + ( + "import sys; " + "sys.path.insert(0, sys.argv[1]); " + "import pip; " + "sys.audit('pip.requirement.source_ready', " + "'demo', None, 'https://example.invalid/demo-1.0.0.tar.gz', " + "sys.argv[2], False, 0)" + ), + str(fake_pip_root), + str(source), + ], + env=env, + ) + + self.assertEqual(module.read_text(encoding="utf-8"), 'value = "old"\n') + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + def test_cli_parse_args_event_adds_graalpy_defaults(self): + with tempfile.TemporaryDirectory(prefix="graalpy-pip-audit-cli-") as tmp: + root = Path(tmp) + fake_pip_root = root / "fake-pip" + fake_pip_root.mkdir() + write_fake_pip(fake_pip_root) + + env = os.environ.copy() + env.pop("PIP_INDEX_URL", None) + env.pop("PIP_CACHE_DIR", None) + + subprocess.check_call( + [ + sys.executable, + "-S", + "-c", + ( + "import sys; " + "from types import SimpleNamespace; " + "sys.path.insert(0, sys.argv[1]); " + "import pip; " + "options = SimpleNamespace(extra_index_urls=[], cache_dir=sys.argv[2]); " + "sys.audit('pip.cli.parse_args', options, [], 0); " + "assert options.extra_index_urls == ['https://www.graalvm.org/python/wheels/']; " + "assert options.cache_dir == sys.argv[2] + '-graalpy'" + ), + str(fake_pip_root), + str(root / "pip-cache"), + ], + env=env, + ) + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + def test_import_function_installs_pip_audit_hook(self): + with tempfile.TemporaryDirectory(prefix="graalpy-pip-audit-import-") as tmp: + root = Path(tmp) + fake_pip_root = root / "fake-pip" + fake_pip_root.mkdir() + write_fake_pip(fake_pip_root) + + env = os.environ.copy() + env.pop("PIP_INDEX_URL", None) + env.pop("PIP_CACHE_DIR", None) + + subprocess.check_call( + [ + sys.executable, + "-S", + "-c", + ( + "import sys; " + "from types import SimpleNamespace; " + "sys.path.insert(0, sys.argv[1]); " + "__import__('pip'); " + "options = SimpleNamespace(extra_index_urls=[], cache_dir=sys.argv[2]); " + "sys.audit('pip.cli.parse_args', options, [], 0); " + "assert options.extra_index_urls == ['https://www.graalvm.org/python/wheels/']; " + "assert options.cache_dir == sys.argv[2] + '-graalpy'" + ), + str(fake_pip_root), + str(root / "pip-cache"), + ], + env=env, + ) + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + def test_find_all_candidates_event_adds_metadata_sources(self): + with tempfile.TemporaryDirectory(prefix="graalpy-pip-audit-candidates-") as tmp: + root = Path(tmp) + patches = root / "patches" + fake_pip_root = root / "fake-pip" + patches.mkdir() + fake_pip_root.mkdir() + write_fake_pip(fake_pip_root) + + (patches / "metadata.toml").write_text( + "\n".join( + [ + "[[demo.add-sources]]", + "version = '1.0.0'", + "url = 'https://example.invalid/demo/archive/refs/tags/v1.0.0.tar.gz'", + ] + ), + encoding="utf-8", + ) + + env = os.environ.copy() + env["PIP_GRAALPY_PATCHES_URL"] = patches.as_uri() + + subprocess.check_call( + [ + sys.executable, + "-S", + "-c", + ( + "import sys; " + "sys.path.insert(0, sys.argv[1]); " + "import pip; " + "extra = []; " + "sys.audit('pip.package_finder.find_all_candidates', 'demo', (), extra); " + "assert extra == [('demo', '1.0.0', {" + "'url': 'https://example.invalid/demo/archive/refs/tags/v1.0.0.tar.gz', " + "'filename': 'demo-1.0.0.tar.gz', " + "'comes_from': 'GraalPy compatibility patches', " + "'requires_python': None, " + "'yanked_reason': None})]" + ), + str(fake_pip_root), + ], + env=env, + ) + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + @unittest.skipUnless(shutil.which("patch"), "requires the patch utility") + def test_source_ready_event_uses_link_filename_for_add_source_urls(self): + with tempfile.TemporaryDirectory(prefix="graalpy-pip-audit-link-filename-") as tmp: + root = Path(tmp) + patches = root / "patches" + source = root / "demo-1.0.0" + fake_pip_root = root / "fake-pip" + patches.mkdir() + source.mkdir() + fake_pip_root.mkdir() + write_fake_pip(fake_pip_root) + + (patches / "metadata.toml").write_text( + "\n".join( + [ + "[[demo.rules]]", + "version = '== 1.0.0'", + "patch = 'demo.patch'", + "license = 'MIT'", + ] + ), + encoding="utf-8", + ) + (patches / "demo.patch").write_text( + "\n".join( + [ + "diff --git a/demo_module.py b/demo_module.py", + "--- a/demo_module.py", + "+++ b/demo_module.py", + "@@ -1 +1 @@", + '-value = "old"', + '+value = "patched"', + "", + ] + ), + encoding="utf-8", + ) + module = source / "demo_module.py" + module.write_text('value = "old"\n', encoding="utf-8") + + env = os.environ.copy() + env["PIP_GRAALPY_PATCHES_URL"] = patches.as_uri() + env.pop("PIP_GRAALPY_DISABLE_PATCHING", None) + + subprocess.check_call( + [ + sys.executable, + "-S", + "-c", + "\n".join( + [ + "import sys", + "sys.path.insert(0, sys.argv[1])", + "import pip", + "class Link:", + " filename = 'demo-1.0.0.tar.gz'", + " def __str__(self):", + " return 'https://example.invalid/demo/archive/refs/tags/v1.0.0.tar.gz'", + "sys.audit(", + " 'pip.requirement.source_ready',", + " 'demo',", + " None,", + " Link(),", + " sys.argv[2],", + " False,", + " 0,", + ")", + ] + ), + str(fake_pip_root), + str(source), + ], + env=env, + ) + + self.assertEqual(module.read_text(encoding="utf-8"), 'value = "patched"\n') + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + def test_source_ready_event_warns_about_suggested_versions(self): + with tempfile.TemporaryDirectory(prefix="graalpy-pip-audit-suggest-") as tmp: + root = Path(tmp) + patches = root / "patches" + source = root / "demo-2.0.0" + fake_pip_root = root / "fake-pip" + patches.mkdir() + source.mkdir() + fake_pip_root.mkdir() + write_fake_pip(fake_pip_root) + + (patches / "metadata.toml").write_text( + "\n".join( + [ + "[[demo.rules]]", + "version = '== 1.0.0'", + "patch = 'demo.patch'", + "license = 'MIT'", + ] + ), + encoding="utf-8", + ) + (patches / "demo.patch").write_text("", encoding="utf-8") + + env = os.environ.copy() + env["PIP_GRAALPY_PATCHES_URL"] = patches.as_uri() + + subprocess.check_call( + [ + sys.executable, + "-S", + "-c", + "\n".join( + [ + "import sys, warnings", + "sys.path.insert(0, sys.argv[1])", + "import pip", + "with warnings.catch_warnings(record=True) as caught:", + " warnings.simplefilter('always')", + " sys.audit(", + " 'pip.requirement.source_ready',", + " 'demo',", + " None,", + " 'https://example.invalid/demo-2.0.0.tar.gz',", + " sys.argv[2],", + " False,", + " 0,", + " )", + " assert any('version(s): == 1.0.0' in str(w.message) for w in caught)", + ] + ), + str(fake_pip_root), + str(source), + ], + env=env, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py index bd13515e34..6511da2dca 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.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 @@ -239,6 +239,15 @@ def __fspath__(self): with self.assertRaisesRegex(TypeError, r"expected Wrap.__fspath__\(\) to return str or bytes, not " + type(x).__name__): os.fspath(Wrap(x)) + class MissingFspath: + def __getattr__(self, name): + if name == "__fspath__": + return lambda: "abc" + raise AttributeError(name) + + with self.assertRaisesRegex(TypeError, "expected str, bytes or os.PathLike object"): + os.fspath(MissingFspath()) + def test_path_convertor(self): class C: def __fspath__(self): @@ -249,6 +258,18 @@ def __fspath__(self): with self.assertRaisesRegex(TypeError, r"expected C.__fspath__\(\) to return str or bytes, not bytearray"): os.open(C(), 0) + def test_open_bytes_path(self): + try: + with open(os.fsencode(TEST_FULL_PATH1), os.O_WRONLY | os.O_CREAT) as fd: + os.write(fd, b'hello') + with open(os.fsencode(TEST_FULL_PATH1), os.O_RDONLY) as fd: + self.assertEqual(b'hello', os.read(fd, 5)) + finally: + try: + os.unlink(TEST_FULL_PATH1) + except Exception: + pass + def test_fd_converter(self): class MyInt(int): def fileno(self): return 0 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_pyexpat_entity_expansion_limit.py b/graalpython/com.oracle.graal.python.test/src/tests/test_pyexpat_entity_expansion_limit.py new file mode 100644 index 0000000000..c182e345fe --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_pyexpat_entity_expansion_limit.py @@ -0,0 +1,91 @@ +# 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. + +import subprocess +import sys +import textwrap +import unittest + + +def entity_expansion_payload(levels=5, fanout=8): + entities = [''] + for level in range(1, levels + 1): + value = f"&e{level - 1};" * fanout + entities.append(f'') + dtd = "\n".join(entities) + return f"&e{levels};".encode() + + +class PyExpatEntityExpansionLimitTest(unittest.TestCase): + + @unittest.skipUnless(sys.implementation.name == "graalpy", "GraalPy-specific test") + def test_java_backend_internal_entity_expansion_is_limited(self): + code = textwrap.dedent(f""" + from xml.parsers import expat + + expat.ParserCreate().Parse({entity_expansion_payload(levels=6)!r}, True) + + parser = expat.ParserCreate() + try: + parser.Parse({entity_expansion_payload(levels=7)!r}, True) + except expat.ExpatError as e: + expected_code = expat.errors.codes[expat.errors.XML_ERROR_AMPLIFICATION_LIMIT_BREACH] + if e.code != expected_code: + raise SystemExit(f"unexpected error code: {{e.code}} != {{expected_code}}") + if expat.ErrorString(e.code) != expat.errors.XML_ERROR_AMPLIFICATION_LIMIT_BREACH: + raise SystemExit(f"unexpected error string: {{expat.ErrorString(e.code)!r}}") + raise SystemExit(0) + raise SystemExit("entity expansion was not limited") + """) + + result = subprocess.run([ + sys.executable, + "--vm.Djdk.xml.entityExpansionLimit=0", + "--vm.Djdk.xml.totalEntitySizeLimit=0", + "--vm.Djdk.xml.entityReplacementLimit=0", + "--python.PyExpatModuleBackend=java", + "-c", + code, + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + + +if __name__ == '__main__': + unittest.main() diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_reparse.py b/graalpython/com.oracle.graal.python.test/src/tests/test_reparse.py index ca2deb4c34..775e43da5c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_reparse.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_reparse.py @@ -49,15 +49,32 @@ import unittest from pathlib import Path -SYNC_PREAMBLE = ''' +SYNC_HOST = "127.0.0.1" +SYNC_TIMEOUT = 180.0 + +SYNC_PREAMBLE = f''' import sys import socket -with socket.create_connection(('localhost', int(sys.argv[1]))) as sock: +with socket.create_connection(({SYNC_HOST!r}, int(sys.argv[1])), timeout={SYNC_TIMEOUT!r}) as sock: sock.recv(1) ''' +def _terminate_and_collect(proc): + if proc.poll() is not None: + return proc.communicate()[0] + try: + proc.terminate() + except ProcessLookupError: + return proc.communicate()[0] + try: + return proc.communicate(timeout=10)[0] + except subprocess.TimeoutExpired: + proc.kill() + return proc.communicate()[0] + + @contextlib.contextmanager def pyc_reparse(test_content, expect_success=True, python_options=()): if sys.implementation.name != "graalpy" or not __graalpython__.is_bytecode_dsl_interpreter: @@ -73,7 +90,7 @@ def pyc_reparse(test_content, expect_success=True, python_options=()): compileall.compile_file(example_module_path, force=True, quiet=True) pyc_files = list((tempdir_path / '__pycache__').glob('*.pyc')) assert len(pyc_files) == 1, "Didn't find a .pyc file" - with socket.create_server(('0.0.0.0', 0)) as server: + with socket.create_server((SYNC_HOST, 0)) as server: port = server.getsockname()[1] env = os.environ.copy() env['PYTHONPATH'] = str(tempdir_path) @@ -84,20 +101,27 @@ def pyc_reparse(test_content, expect_success=True, python_options=()): stderr=subprocess.STDOUT, text=True, ) - server.settimeout(3.0) - retries = 20 - while retries: + deadline = time.monotonic() + SYNC_TIMEOUT + while True: + remaining = deadline - time.monotonic() + if remaining <= 0: + out = _terminate_and_collect(proc) + assert False, f"Timed out waiting for connection after {SYNC_TIMEOUT:.0f}s\n{out}" + server.settimeout(min(3.0, remaining)) try: with server.accept()[0] as sock: - yield example_module_path, pyc_files[0] - sock.sendall(b"x") + try: + yield example_module_path, pyc_files[0] + finally: + sock.sendall(b"x") break except socket.timeout: assert proc.poll() is None, proc.communicate()[0] - retries -= 1 - else: - assert False, "Timed out wating for connection" - out = proc.communicate()[0] + try: + out = proc.communicate(timeout=SYNC_TIMEOUT)[0] + except subprocess.TimeoutExpired: + out = _terminate_and_collect(proc) + assert False, f"Timed out waiting for child process after {SYNC_TIMEOUT:.0f}s\n{out}" if expect_success: assert proc.wait() == 0, out else: diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_repl.py b/graalpython/com.oracle.graal.python.test/src/tests/test_repl.py index 05f1fce96f..b5e1b66704 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_repl.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_repl.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 @@ -47,6 +47,7 @@ import unittest from tests import util from dataclasses import dataclass +from functools import wraps from textwrap import dedent if (sys.platform != 'win32' and (sys.platform != 'linux' or platform.machine() != 'aarch64')) and ( @@ -54,6 +55,7 @@ # The terminal tests can be flaky def autoretry(fn): + @wraps(fn) def decorated(*args, **kwargs): retries = 3 while retries: @@ -77,7 +79,7 @@ class ExpectedInOutItem: @autoretry - def validate_repl(stdin, python_args=(), ignore_preamble=True): + def validate_repl(stdin, python_args=(), ignore_preamble=True, extra_input_and_output=()): env = os.environ.copy() env['TERM'] = 'ansi' env['PYTHONIOENCODING'] = 'utf-8' @@ -103,6 +105,7 @@ def validate_repl(stdin, python_args=(), ignore_preamble=True): expected_input = match.group(2) expected_output = stdin[match.end():in_matches[i + 1].start() - 1 if i + 1 < len(in_matches) else -1] input_and_output.append(ExpectedInOutItem(prompt, expected_input, expected_output)) + input_and_output.extend(extra_input_and_output) index = -1 whole_out = '' while True: @@ -111,9 +114,12 @@ def validate_repl(stdin, python_args=(), ignore_preamble=True): out += os.read(pty_parent, 1024).decode('utf-8') out = re.sub(r'\x1b\[(?:\?2004[hl]|\d+[A-G])', '', out) out = re.sub(r'\r+\n', '\n', out) - if out == '>>> ' or out.endswith(('\n>>> ', '\n... ')): + if out.endswith(('\n... ', '>>> ')): prompt = out[:3] - actual = out[:-5] + actual_end = len(out) - 4 + if actual_end > 0 and out[actual_end - 1] == '\n': + actual_end -= 1 + actual = out[:actual_end] if index >= 0: current = input_and_output[index] assert prompt == current.prompt, f"Actual prompt: {prompt}\nExpected prompt: {current.prompt}" @@ -126,7 +132,7 @@ def validate_repl(stdin, python_args=(), ignore_preamble=True): whole_out += out[:-4] out = out[-4:] if index >= len(input_and_output): - os.write(pty_parent, b'\x04') # CTRL-D + os.write(pty_parent, b'\x04') # Ctrl-D / EOF proc.wait(timeout=60) out = os.read(pty_parent, 1024).decode('utf-8') out = re.sub(r'\x1b\[\?2004[hl]', '', out) @@ -165,6 +171,15 @@ def test_basic_repl_no_readline(): """), python_args=['-I']) + @autoretry + def test_repl_flushes_std_streams(): + validate_repl("", extra_input_and_output=[ + ExpectedInOutItem('>>>', '40 + 2', '\n42'), + ExpectedInOutItem('>>>', '_ = __import__("sys").stderr.write("stderr")', '\nstderr'), + ExpectedInOutItem('>>>', '_ = __import__("sys").stdout.write("stdout")', '\nstdout'), + ]) + + def test_continuation(): validate_repl(dedent(r'''\ >>> def foo(): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_repr.py b/graalpython/com.oracle.graal.python.test/src/tests/test_repr.py index 514b5a351e..a99aa531a3 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_repr.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_repr.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2021, 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 @@ -37,6 +37,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from collections import UserDict, UserList +from test.support import set_recursion_limit + + +REPR_RECURSION_LIMIT = 100 + def assert_not_raises(fnc, *args, **kwargs): try: @@ -66,3 +72,39 @@ def a_func(): assert_not_raises(lambda: repr(a_func)) assert_not_raises(lambda: repr(object())) assert_not_raises(lambda: repr(x)) + + +def test_repr_type_error_includes_returned_value(): + class ReprReturnsInt: + def __repr__(self): + return 42 + + try: + repr(ReprReturnsInt()) + assert False + except TypeError as e: + assert str(e) == "__repr__ returned non-string (type int)" + + +def test_repr_deep_userlist_raises_recursion_error(): + a = UserList([]) + for _ in range(REPR_RECURSION_LIMIT + 10): + a = UserList([a]) + with set_recursion_limit(REPR_RECURSION_LIMIT): + try: + repr(a) + assert False + except RecursionError: + pass + + +def test_repr_deep_userdict_raises_recursion_error(): + d = UserDict() + for _ in range(REPR_RECURSION_LIMIT + 10): + d = UserDict({1: d}) + with set_recursion_limit(REPR_RECURSION_LIMIT): + try: + repr(d) + assert False + except RecursionError: + pass diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_resource.py b/graalpython/com.oracle.graal.python.test/src/tests/test_resource.py index 5133726973..4ae2b0179b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_resource.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_resource.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2024, 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 @@ -37,9 +37,18 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import sys +import unittest + +try: + _POSIX_MODULE_BACKEND = __graalpython__.posix_module_backend() +except Exception: + _POSIX_MODULE_BACKEND = "cpython" + +_NATIVE_POSIX_LINUX = sys.platform == "linux" and _POSIX_MODULE_BACKEND == "native" + def test_import(): - import sys if sys.platform not in ['darwin', 'linux']: return imported = True @@ -50,7 +59,7 @@ def test_import(): assert imported -def test_gerusage(): +def test_getrusage(): from resource import getrusage, RUSAGE_SELF try: from resource import RUSAGE_THREAD @@ -66,3 +75,18 @@ def test_gerusage(): assert ru.ru_utime >= 0 assert ru.ru_stime >= 0 assert ru.ru_maxrss > 0 + + +@unittest.skipUnless(_NATIVE_POSIX_LINUX, "Requires native POSIX backend on Linux") +def test_gerusage_cpu_time_progress(): + import time + from resource import getrusage, RUSAGE_SELF + + start = getrusage(RUSAGE_SELF) + deadline = time.monotonic() + 0.25 + value = 0 + while time.monotonic() < deadline: + value = (value * 3 + 1) % 1000003 + end = getrusage(RUSAGE_SELF) + assert value >= 0 + assert (end.ru_utime + end.ru_stime) - (start.ru_utime + start.ru_stime) > 1e-3 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_set.py b/graalpython/com.oracle.graal.python.test/src/tests/test_set.py index 2f587532ff..4fdd291dc9 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_set.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_set.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 @@ -596,29 +596,68 @@ def key1_eq_call(key1, key2): test_op(operator.__and__, key1_eq_call) test_op(operator.__iand__, key1_eq_call) - # TODO: GR-42240 - # - # def symmetric_difference_check(key1, key2): - # assert key1.eq_calls == 0 - # assert key1.hash_calls == 0 - # assert key2.eq_calls == 2 - # assert key2.hash_calls == 0 - # - # test_op(set.symmetric_difference, symmetric_difference_check) - # test_op(operator.__xor__, symmetric_difference_check) - # test_op(operator.__ixor__, symmetric_difference_check) - # - # def symmetric_difference_update_check(key1, key2): - # assert key1.eq_calls == 2 - # assert key1.hash_calls == 0 - # assert key2.eq_calls == 0 - # assert key2.hash_calls == 0 - # - # test_op(set.symmetric_difference_update, symmetric_difference_update_check) - # + def symmetric_difference_check(key1, key2): + assert key1.eq_calls == 0 + assert key1.hash_calls == 0 + assert key2.eq_calls == 2 + assert key2.hash_calls == 0 + + test_op(set.symmetric_difference, symmetric_difference_check) + test_op(operator.__xor__, symmetric_difference_check) + + def symmetric_difference_update_check(key1, key2): + assert key1.eq_calls == 2 + assert key1.hash_calls == 0 + assert key2.eq_calls == 0 + assert key2.hash_calls == 0 + + test_op(operator.__ixor__, symmetric_difference_update_check) + test_op(set.symmetric_difference_update, symmetric_difference_update_check) + # TODO: intersection, intersection_update +def test_symmetric_difference_dict_keys_side_effects(): + def test_op(op, check): + key1 = TrackingKey('foo', hash=42) + key2 = TrackingKey('bar', hash=42) + s = {key1} + d = {key2: 1} + key1.clear_observations() + key2.clear_observations() + op(s, d.keys()) + check(key1, key2) + + def symmetric_difference_check(key1, key2): + assert key1.eq_calls == 0 + assert key1.hash_calls == 0 + assert key2.eq_calls == 2 + assert key2.hash_calls == 1 + + def symmetric_difference_update_check(key1, key2): + assert key1.eq_calls == 2 + assert key1.hash_calls == 0 + assert key2.eq_calls == 0 + assert key2.hash_calls == 1 + + test_op(set.symmetric_difference, symmetric_difference_check) + test_op(set.symmetric_difference_update, symmetric_difference_update_check) + + +def test_symmetric_difference_update_empty_side_effects(): + key1 = TrackingKey('foo', hash=42) + key2 = TrackingKey('bar', hash=42) + s = {key1, key2} + key1.clear_observations() + key2.clear_observations() + + s.symmetric_difference_update(set()) + assert key1.eq_calls == 0 + assert key1.hash_calls == 0 + assert key2.eq_calls == 0 + assert key2.hash_calls == 0 + + def test_pop_side_effects(): class TrackingKey: def __init__(self): @@ -647,3 +686,40 @@ def test_set_iterator_reduce(): it = s.__iter__() it.__reduce__() assert [i for i in it] == [1, 2, 3] + + +def test_set_operations_return_builtin_set_for_subclass(): + class SetSubclass(set): + pass + + s = SetSubclass([1, 2]) + other = {2, 3} + + assert type(s.copy()) is set + assert type(s | other) is set + assert type(other | s) is set + assert type(s & other) is set + assert type(s - other) is set + assert type(s ^ other) is set + assert type(s.union(other)) is set + assert type(s.intersection(other)) is set + assert type(s.difference(other)) is set + assert type(s.symmetric_difference(other)) is set + + +def test_frozenset_operations_return_builtin_frozenset_for_subclass(): + class FrozenSetSubclass(frozenset): + pass + + f = FrozenSetSubclass([1, 2]) + other = {2, 3} + + assert type(f.copy()) is frozenset + assert type(f | other) is frozenset + assert type(f & other) is frozenset + assert type(f - other) is frozenset + assert type(f ^ other) is frozenset + assert type(f.union(other)) is frozenset + assert type(f.intersection(other)) is frozenset + assert type(f.difference(other)) is frozenset + assert type(f.symmetric_difference(other)) is frozenset diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_signal.py b/graalpython/com.oracle.graal.python.test/src/tests/test_signal.py index c46b265b5b..a3fdde3fa6 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_signal.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_signal.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,6 +82,84 @@ def handler(signum, frame): assert False, "Signal handler didn't trigger or propagate exception" +def test_itimer(): + assert signal.getitimer(signal.ITIMER_REAL) == (0.0, 0.0) + try: + old = signal.setitimer(signal.ITIMER_REAL, 1.0, 0.25) + assert old == (0.0, 0.0), old + current = signal.getitimer(signal.ITIMER_REAL) + assert 0.0 <= current[0] <= 1.0, current + assert current[1] == 0.25, current + old = signal.setitimer(signal.ITIMER_REAL, 0) + assert 0.0 <= old[0] <= 1.0, old + assert old[1] == 0.25, old + finally: + signal.setitimer(signal.ITIMER_REAL, 0) + + for func, args in ( + (signal.getitimer, (-1,)), + (signal.setitimer, (-1, 0)), + (signal.setitimer, (signal.ITIMER_REAL, -1)), + ): + try: + func(*args) + except signal.ItimerError as e: + assert e.errno == 22, e + else: + raise AssertionError(f"{func.__name__}{args} did not raise ItimerError") + + +def test_emulated_timers_use_current_handler(): + if sys.implementation.name != 'graalpy' or __graalpython__.posix_module_backend() != 'java': + return + + import time + + calls = [] + + def first(signum, frame): + calls.append(("first", signum, frame)) + + def second(signum, frame): + calls.append(("second", signum, frame)) + + def wait_for_call(timeout=2.5): + deadline = time.time() + timeout + while time.time() < deadline: + if calls: + return True + time.sleep(0.01) + return False + + old_handler = signal.signal(signal.SIGALRM, first) + try: + signal.alarm(1) + signal.signal(signal.SIGALRM, second) + assert wait_for_call(), "alarm did not trigger handler" + assert calls[0][0] == "second", calls + assert calls[0][1] == signal.SIGALRM, calls + assert calls[0][2].f_code.co_name + + calls.clear() + signal.signal(signal.SIGALRM, first) + signal.setitimer(signal.ITIMER_REAL, 0.05) + signal.signal(signal.SIGALRM, second) + assert wait_for_call(), "setitimer did not trigger handler" + assert calls[0][0] == "second", calls + assert calls[0][1] == signal.SIGALRM, calls + assert calls[0][2].f_code.co_name + + calls.clear() + signal.signal(signal.SIGALRM, signal.SIG_IGN) + signal.setitimer(signal.ITIMER_REAL, 0.05) + time.sleep(0.2) + assert calls == [] + finally: + signal.alarm(0) + signal.setitimer(signal.ITIMER_REAL, 0) + signal.signal(signal.SIGALRM, old_handler) + + def test_interrupt(): if sys.implementation.name == 'graalpy' and __graalpython__.posix_module_backend() == 'java': # Sending SIGINT does not work when using the Java backend for posix diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_socket.py b/graalpython/com.oracle.graal.python.test/src/tests/test_socket.py index b9a10bbb8b..12b7826de8 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_socket.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_socket.py @@ -1,4 +1,4 @@ -# 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 @@ -59,6 +59,13 @@ def test_inet_aton_errs(self): self.assertRaises(OSError, lambda : socket.inet_aton('255.255.256.1')) self.assertRaises(TypeError, lambda : socket.inet_aton(255)) + +class TestHostLookupErrors(unittest.TestCase): + def test_gethostbyname_ex_invalid_host_raises_gaierror(self): + with self.assertRaises(socket.gaierror): + socket.gethostbyname_ex("nonexistent.invalid") + + def test_get_name_info(): import socket try : @@ -103,3 +110,33 @@ def server(): sock.recv_into(buffer, 1) assert b == b'123' thread.join() + + +def test_sendto_recvfrom_roundtrip(): + try: + if __graalpython__.posix_module_backend() == "java": + return + except NameError: + pass + + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server: + server.bind(("127.0.0.1", 0)) + server_addr = server.getsockname() + + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client: + client.bind(("127.0.0.1", 0)) + client_addr = client.getsockname() + + sent = client.sendto(b"phase5", server_addr) + assert sent == 6 + + data, addr = server.recvfrom(64) + assert data == b"phase5" + assert addr == client_addr + + sent = server.sendto(b"reply", client_addr) + assert sent == 5 + + data, addr = client.recvfrom(64) + assert data == b"reply" + assert addr == server_addr 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..c4614bce56 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 needs_capi + + +@needs_capi 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() +@needs_capi 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_ssl.py b/graalpython/com.oracle.graal.python.test/src/tests/test_ssl.py index 1a701cb126..daf4fa5fbb 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_ssl.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_ssl.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021, 2022, 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 @@ -372,6 +372,18 @@ def test_private_key_pkcs1(self): def test_private_key_pkcs1_password(self): self.check_keypair("signed_cert_pkcs1_password.pem", "signing_ca.pem", password="password") + def test_private_key_ec_legacy(self): + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.load_cert_chain(data_file("keycertecc.pem"), keyfile=data_file("pk_ecc_legacy.pem")) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.check_hostname = False + client_context.verify_mode = ssl.CERT_NONE + check_handshake(server_context, client_context) + + def test_private_key_dsa_legacy(self): + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + server_context.load_cert_chain(data_file("cert_dsa.pem"), keyfile=data_file("pk_dsa_legacy.pem")) + def test_alpn(self): signed_cert = data_file("signed_cert.pem") server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) @@ -393,6 +405,30 @@ def test_alpn(self): server, client = check_handshake(server_context, client_context) self.assertEqual(client.selected_alpn_protocol(), "http/1.1") + +class ProtocolSelectionTests(unittest.TestCase): + + def check_protocol_availability_controls_selection(self, has_name, protocol_name, protocol_id): + if getattr(ssl, has_name): + self.assertTrue(hasattr(ssl, protocol_name)) + ssl.SSLContext(getattr(ssl, protocol_name)) + else: + protocol = getattr(ssl, protocol_name, protocol_id) + with self.assertRaisesRegex(ValueError, "invalid or unsupported protocol version"): + ssl.SSLContext(protocol) + + def test_single_version_availability_controls_selection(self): + self.check_protocol_availability_controls_selection("HAS_SSLv3", "PROTOCOL_SSLv3", 1) + self.check_protocol_availability_controls_selection("HAS_TLSv1", "PROTOCOL_TLSv1", 3) + self.check_protocol_availability_controls_selection("HAS_TLSv1_1", "PROTOCOL_TLSv1_1", 4) + self.check_protocol_availability_controls_selection("HAS_TLSv1_2", "PROTOCOL_TLSv1_2", 5) + + def test_generic_protocols_keep_sslv3_disabled(self): + for protocol in (ssl.PROTOCOL_TLS, ssl.PROTOCOL_TLS_CLIENT, ssl.PROTOCOL_TLS_SERVER): + with self.subTest(protocol=protocol): + self.assertTrue(ssl.SSLContext(protocol).options & ssl.OP_NO_SSLv3) + + def get_cipher_list(cipher_string): context = ssl.SSLContext() context.set_ciphers(cipher_string) @@ -426,3 +462,7 @@ def test_error(self): get_cipher_list("ALL:!ALL:ADH") with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"): get_cipher_list("ALL:@XXX") + import sys + if sys.implementation.name == "graalpy": + with self.assertRaisesRegex(NotImplementedError, "only @SECLEVEL=1 is supported"): + get_cipher_list("@SECLEVEL=2:ALL") diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_startup.py b/graalpython/com.oracle.graal.python.test/src/tests/test_startup.py index cc1f09d552..c98e7057fc 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_startup.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_startup.py @@ -44,6 +44,19 @@ import platform # Both lists should remain as small as possible to avoid adding overhead to startup +IS_WINDOWS = platform.system() == 'Windows' +WINDOWS_CORE_MODULES = ['_nt', '_winapi', '_overlapped', 'winreg', '_winreg'] if IS_WINDOWS else [] +WINDOWS_FULL_STARTUP_MODULES = [ + '_datetime', + 'datetime', + '_ctypes', + '_struct', + 'struct', + 'ctypes._endian', + 'ctypes', + 'ctypes.wintypes', +] if IS_WINDOWS else [] + expected_nosite_startup_modules = [ '_frozen_importlib', '_frozen_importlib_external', @@ -51,8 +64,7 @@ '_sre', '_sysconfig', 'java', - 'pip_hook', -] + (['_nt'] if platform.system() == 'Windows' else []) +] + WINDOWS_CORE_MODULES expected_full_startup_modules = expected_nosite_startup_modules + [ '_abc', @@ -63,11 +75,12 @@ 'stat', '_collections_abc', 'genericpath', - *(['_winapi', 'ntpath'] if platform.system() == 'Windows' else ['posixpath']), + *(['ntpath'] if IS_WINDOWS else ['posixpath']), 'os', '_sitebuiltins', '_io', 'io', + *WINDOWS_FULL_STARTUP_MODULES, 'site', ] @@ -76,12 +89,12 @@ class StartupTests(unittest.TestCase): def test_startup_nosite(self): result = subprocess.check_output([sys.executable, '--log.level=FINE', '-S', '-v', '-c', 'print("Hello")'], stderr=subprocess.STDOUT, text=True) assert 'Hello' in result - imports = re.findall("import '(\S+)'", result) + imports = re.findall(r"import '(\S+)'", result) self.assertEqual(expected_nosite_startup_modules, imports) @unittest.skipUnless(sys.implementation.name == 'graalpy', "GraalPy-specific test") def test_startup_full(self): result = subprocess.check_output([sys.executable, '--log.level=FINE', '-s', '-v', '-c', 'print("Hello")'], stderr=subprocess.STDOUT, text=True) assert 'Hello' in result - imports = re.findall("import '(\S+)'", result) + imports = re.findall(r"import '(\S+)'", result) self.assertEqual(expected_full_startup_modules, imports) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_string.py b/graalpython/com.oracle.graal.python.test/src/tests/test_string.py index aea27924f9..e8f02c9ada 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_string.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_string.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 @@ -168,6 +168,10 @@ def test_strip(): assert u' test '.strip() == u'test' +def test_splitlines_keepends_type_error(): + assert "foo\n".splitlines("bla") == ["foo\n"] + + def assertEqual(value, expected): assert value == expected, ("'%s' was expected to be equal to '%s'" % (value, expected)) @@ -1095,6 +1099,12 @@ def test_splitlines(): assert len(str.splitlines("a\nb")) == 2 +def test_split_negative_maxsplit_matches_unlimited(): + s = "0x1.e800000000000p+5" + assert s.split(s, maxsplit=-84) == ["", ""] + assert s.split(s, maxsplit=-1) == ["", ""] + + def test_literals(): s = "hello\[world\]" assert len(s) == 14 @@ -1233,4 +1243,4 @@ def test_fstring(): assert type(f"hello {FunkyFormat('world')}!") == str assert f"hello {FunkyFormat('world')}!" == "hello world!" assert f"hello {FunkyStr('world')}!" == "hello world!" - assertRaises(TypeError, lambda: f"hello {FunkyFormat(33)}!") \ No newline at end of file + assertRaises(TypeError, lambda: f"hello {FunkyFormat(33)}!") diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_struct.py b/graalpython/com.oracle.graal.python.test/src/tests/test_struct.py index 4fde738440..b0a9b26cb9 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_struct.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_struct.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, 2024, 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 @@ -187,6 +187,14 @@ def test_pack_large_long(): assert struct.unpack(fmt, b'\xff' * size) == (maxval,) +def test_pack_void_ptr_unsigned_range(): + size = struct.calcsize('P') + maxval = (1 << (size * 8)) - 1 + assert struct.pack('P', maxval) == b'\xff' * size + assert struct.unpack('P', struct.pack('P', -1)) == (maxval,) + assert_raises(struct.error, struct.pack, 'P', maxval + 1) + + def test_pack_into(): test_string = b'Reykjavik rocks, eow!' writable_buf = bytearray(b' '*100) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_subprocess.py b/graalpython/com.oracle.graal.python.test/src/tests/test_subprocess.py index b7f2dc1dea..80d7f523a6 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_subprocess.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_subprocess.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 @@ -11,7 +11,9 @@ from subprocess import CalledProcessError from tempfile import mkdtemp -POSIX_BACKEND_IS_JAVA = sys.implementation.name == "graalpy" and __graalpython__.posix_module_backend != 'java' +from tests.util import run_subprocess_with_graalpy_startup_retry + +POSIX_BACKEND_IS_JAVA = sys.implementation.name == "graalpy" and __graalpython__.posix_module_backend() == 'java' def test_os_pipe(): import os @@ -75,6 +77,17 @@ def test_check_output(self): [sys.executable, "-c", "print('BDFL')"]) self.assertIn(b'BDFL', output) + @unittest.skipIf(sys.platform == 'win32', "POSIX argv bytes specific") + def test_surrogateescape_non_utf8_argv(self): + code = ( + "import os, sys; " + "assert os.fsencode(sys.argv[-1]) == b'\\x8av'; " + "print(repr(sys.argv[-1]))" + ) + cmd = f"{shlex.quote(sys.executable)} -c {shlex.quote(code)} \"$(printf '\\212v')\"" + output = subprocess.check_output(cmd, shell=True, stderr=subprocess.PIPE, text=True) + self.assertEqual("'\\udc8av'\n", output) + def test_check_output_nonzero(self): # check_call() function with non-zero return code with self.assertRaises(subprocess.CalledProcessError) as c: @@ -107,6 +120,14 @@ def test_waitpid(self): p.kill() p.wait() + @unittest.skipIf(sys.platform == 'win32' or POSIX_BACKEND_IS_JAVA or not hasattr(os, 'getpgid'), "Posix-specific") + def test_process_group_0(self): + p = subprocess.Popen([sys.executable, "-c", "import os; print(os.getpgid(0))"], + stdout=subprocess.PIPE, process_group=0) + stdout, _ = p.communicate(timeout=60) + self.assertEqual(p.returncode, 0) + self.assertEqual(int(stdout), p.pid) + def _run_in_new_group(self, code): def safe_decode(x): return '' if not x else x.decode().strip() @@ -122,6 +143,7 @@ def safe_decode(x): print("===== stderr:") print(safe_decode(e.stderr)) print("=============") + raise finally: if filename: os.remove(filename) @@ -137,8 +159,7 @@ def test_waitpid_group_child(self): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) ps_list = 'not available' - if sys.implementation.name == "graalpy" and \\ - and sys.platform.startswith("linux"): + if sys.implementation.name == "graalpy" and sys.platform.startswith("linux"): ps_list = subprocess.check_output("ps", shell=True, text=True) res = os.waitpid(0, 0) msg = f"Spawned {p.pid=}, os.waitpid result={res}, output of ps:\\n{ps_list}" @@ -217,49 +238,52 @@ def test_subprocess_inherits_environ(self): @unittest.skipIf(sys.platform == 'win32', "TODO the cmd replacement breaks the test") def test_graal_python_args(self): if sys.implementation.name == "graalpy": - import subprocess + def env_with_graal_python_args(args): + env = os.environ.copy() + env["GRAAL_PYTHON_ARGS"] = args + return env - env = {"GRAAL_PYTHON_ARGS": "-c 12"} - result = subprocess.run([sys.executable], env=env) - self.assertEqual(0, result.returncode) + env = env_with_graal_python_args("-c 12") + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True) + self.assertEqual(0, result.returncode, result.stderr) - env = {"GRAAL_PYTHON_ARGS": "-c 'print(12)'"} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args("-c 'print(12)'") + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual('12\n', result) - env = {"GRAAL_PYTHON_ARGS": """-c 'print("Hello world")'"""} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args("""-c 'print("Hello world")'""") + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual('Hello world\n', result) - env = {"GRAAL_PYTHON_ARGS": """-c ""'print("Hello world")'"""""} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args("""-c ""'print("Hello world")'""""") + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual('Hello world\n', result) - env = {"GRAAL_PYTHON_ARGS": r"""-c 'print(\'"Hello world"\')'"""""} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args(r"""-c 'print(\'"Hello world"\')'""") + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual('"Hello world"\n', result) - env = {"GRAAL_PYTHON_ARGS": """\v-c\vprint('"Hello world"')"""} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args("""\v-c\vprint('"Hello world"')""") + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual('"Hello world"\n', result) - env = {"GRAAL_PYTHON_ARGS": """\v-c\vprint('Hello', "world")"""} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args("""\v-c\vprint('Hello', "world")""") + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual('Hello world\n', result) # check that the subprocess receives the args and thus it should fail because it recurses args = """\v-c\vimport os\nprint(os.environ.get("GRAAL_PYTHON_ARGS"))""" - env = {"GRAAL_PYTHON_ARGS": args} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args(args) + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual(f"{args}\n", result) # check that the subprocess does not receive the args when we end with \v - env = {"GRAAL_PYTHON_ARGS": """\v-c\vimport os\nprint(os.environ.get("GRAAL_PYTHON_ARGS"))\v"""} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args("""\v-c\vimport os\nprint(os.environ.get("GRAAL_PYTHON_ARGS"))\v""") + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual('None\n', result) # check that the subprocess receives an empty arg args = """\v-c\vimport sys\nprint(repr(sys.argv))\va1\v\va3""" - env = {"GRAAL_PYTHON_ARGS": args} - result = subprocess.check_output([sys.executable], env=env, text=True) + env = env_with_graal_python_args(args) + result = run_subprocess_with_graalpy_startup_retry([sys.executable], env=env, text=True, check=True).stdout self.assertEqual("['-c', 'a1', '', 'a3']\n", result) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_super.py b/graalpython/com.oracle.graal.python.test/src/tests/test_super.py index d518468b5e..7a441031e8 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_super.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_super.py @@ -1,4 +1,4 @@ -# 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 @@ -47,3 +47,44 @@ def m(self): return ["B"] + self.my_super.m() B.my_super = B.my_super assert B().m() == ["B", "A"] + + +def test_super_subclass_descr_get_invokes_subclass_type(): + class MySuper(super): + news = [] + calls = [] + + def __new__(cls, *args): + cls.news.append(args) + return super().__new__(cls) + + def __init__(self, *args): + type(self).calls.append(args) + super().__init__(*args) + + class A: + def f(self): + return "A.f" + + class B(A): + pass + + raw = MySuper(B) + MySuper.news.clear() + MySuper.calls.clear() + obj = B() + bound = raw.__get__(obj, B) + + assert type(bound) is MySuper + assert MySuper.news == [(B, obj)] + assert MySuper.calls == [(B, obj)] + assert bound.f() == "A.f" + + raw = MySuper.__new__(MySuper) + MySuper.news.clear() + MySuper.calls.clear() + bound = raw.__get__(obj, B) + + assert type(bound) is MySuper + assert MySuper.news == [()] + assert MySuper.calls == [()] diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_sys_settrace.py b/graalpython/com.oracle.graal.python.test/src/tests/test_sys_settrace.py index 13fcc48b82..a4bb8236e7 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_sys_settrace.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_sys_settrace.py @@ -37,7 +37,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import os import unittest import difflib import sys @@ -47,6 +46,11 @@ import asyncio +GRAALPY_POSIX_BACKEND_IS_JAVA = ( + hasattr(builtins, '__graalpython__') and builtins.__graalpython__.posix_module_backend() == 'java' +) + + def basic(): return 'return value' @@ -138,6 +142,32 @@ def generator_example(): (4, 'generator_example', 'line', None), (4, 'generator_example', 'return', False)])] +def gen_same_line(): # line -4 + yield 1; x = 2; yield x + + +def generator_same_line_example(): + x = gen_same_line() + a = next(x) + b = next(x) + c = next(x, None) + return a, b, c + +generator_same_line_example.events = [((), [(0, 'generator_same_line_example', 'call', None), + (1, 'generator_same_line_example', 'line', None), + (2, 'generator_same_line_example', 'line', None), + (-4, 'gen_same_line', 'call', None), + (-3, 'gen_same_line', 'line', None), + (-3, 'gen_same_line', 'return', 1), + (3, 'generator_same_line_example', 'line', None), + (-3, 'gen_same_line', 'call', None), + (-3, 'gen_same_line', 'return', 2), + (4, 'generator_same_line_example', 'line', None), + (-3, 'gen_same_line', 'call', None), + (-3, 'gen_same_line', 'return', None), + (5, 'generator_same_line_example', 'line', None), + (5, 'generator_same_line_example', 'return', (1, 2, None))])] + def f_trace_delete(): del sys._getframe().f_trace return 1 @@ -162,7 +192,6 @@ def test_case(self): return test_case -@unittest.skipUnless(os.environ.get('BYTECODE_DSL_INTERPRETER') == 'false', "TODO: FrameSlotTypeException with reparsing") class TraceTests(unittest.TestCase): def trace(self, frame, event, arg): code = frame.f_code @@ -178,6 +207,7 @@ def trace(self, frame, event, arg): # test_03_oneline_loop = make_test_method(oneline_loop, 'test_03_oneline_loop') test_04_two_functions = make_test_method(two_functions, 'test_04_two_functions') test_05_generator_example = make_test_method(generator_example, 'test_05_generator_example') + test_05_generator_same_line_example = make_test_method(generator_same_line_example, 'test_05_generator_same_line_example') def test_06_f_trace_preserved(self): def erroring_trace(*_): raise ValueError @@ -212,6 +242,7 @@ def simpler_trace(self, fr, ev, arg): @unittest.skipIf(not hasattr(signal, 'SIGUSR1'), "User defined signal not present") @unittest.skipIf(not hasattr(builtins, '__graalpython__'), "async actions do get traced in CPython") + @unittest.skipIf(GRAALPY_POSIX_BACKEND_IS_JAVA, "signal.raise_signal is not supported by the Java POSIX backend") def test_07_async_actions_not_traced(self): def handler(*_): handler.called = 1 @@ -488,6 +519,150 @@ def func(): self.assert_events(self.events, events) +class AsyncTracingEventsUnitTest(TracingEventsUnitTest): + def async_trace(self, frame, event, arg): + code = frame.f_code + name = code.co_name + if name in self.names: + self.events.append((frame.f_lineno - self.first_line, name, event)) + return self.async_trace + + async def async_wrapper(self, afunc, names): + self.names = names + sys.settrace(self.async_trace) + try: + await afunc() + finally: + sys.settrace(None) + self.names = None + + def trace_async_function(self, afunc, names): + self.first_line = afunc.__code__.co_firstlineno + self.events = [] + asyncio.run(self.async_wrapper(afunc, names)) + + @unittest.skipIf( + sys.implementation.name == "graalpy", + "GR-65570: GraalPy does not report await exception trace events yet.", + ) + def test_01_await(self): + async def helper(): # line -3 + return 42 + + async def afunc(): + value = await helper() + return value + + self.trace_async_function(afunc, {"afunc", "helper"}) + + events = [ + (0, 'afunc', 'call'), + (1, 'afunc', 'line'), + (-3, 'helper', 'call'), + (-2, 'helper', 'line'), + (-2, 'helper', 'return'), + (1, 'afunc', 'exception'), + (2, 'afunc', 'line'), + (2, 'afunc', 'return'), + ] + + self.assert_events(self.events, events) + + @unittest.skipIf( + sys.implementation.name == "graalpy", + "GR-65570: GraalPy does not report async with exception trace events yet.", + ) + def test_02_async_with(self): + class A: + async def __aenter__(self): + return self + async def __aexit__(self, exc_type, exc, tb): + pass + + async def afunc(): + async with A(): + pass + + self.trace_async_function(afunc, {"afunc", "__aenter__", "__aexit__"}) + + events = [ + (0, 'afunc', 'call'), + (1, 'afunc', 'line'), + (-5, '__aenter__', 'call'), + (-4, '__aenter__', 'line'), + (-4, '__aenter__', 'return'), + (1, 'afunc', 'exception'), + (2, 'afunc', 'line'), + (1, 'afunc', 'line'), + (-3, '__aexit__', 'call'), + (-2, '__aexit__', 'line'), + (-2, '__aexit__', 'return'), + (1, 'afunc', 'exception'), + (1, 'afunc', 'return'), + ] + + self.assert_events(self.events, events) + + @unittest.skipIf( + sys.implementation.name == "graalpy", + "GR-65570: GraalPy does not match CPython async for trace events yet.", + ) + def test_03_async_for(self): + class AsyncIterator: + def __init__(self, items): + self.items = iter(items) + def __aiter__(self): + return self + async def __anext__(self): + try: + return next(self.items) + except StopIteration: + raise StopAsyncIteration + + async def afunc(): + total = 0 + async for item in AsyncIterator([1, 2]): + total += item + return total + + self.trace_async_function(afunc, {"afunc", "__aiter__", "__anext__"}) + + events = [ + (0, 'afunc', 'call'), + (1, 'afunc', 'line'), + (2, 'afunc', 'line'), + (-8, '__aiter__', 'call'), + (-7, '__aiter__', 'line'), + (-7, '__aiter__', 'return'), + (-6, '__anext__', 'call'), + (-5, '__anext__', 'line'), + (-4, '__anext__', 'line'), + (-4, '__anext__', 'return'), + (2, 'afunc', 'exception'), + (3, 'afunc', 'line'), + (2, 'afunc', 'line'), + (-6, '__anext__', 'call'), + (-5, '__anext__', 'line'), + (-4, '__anext__', 'line'), + (-4, '__anext__', 'return'), + (2, 'afunc', 'exception'), + (3, 'afunc', 'line'), + (2, 'afunc', 'line'), + (-6, '__anext__', 'call'), + (-5, '__anext__', 'line'), + (-4, '__anext__', 'line'), + (-4, '__anext__', 'exception'), + (-3, '__anext__', 'line'), + (-2, '__anext__', 'line'), + (-2, '__anext__', 'exception'), + (-2, '__anext__', 'return'), + (2, 'afunc', 'exception'), + (4, 'afunc', 'line'), + (4, 'afunc', 'return'), + ] + + self.assert_events(self.events, events) + class MultilineCallsTraceTest(TracingEventsUnitTest): class A: def m_basic(self, a1, a2, a3): @@ -1291,4 +1466,88 @@ def func(): (3, 'func', 'return') ] - self.assert_events(self.events, events) \ No newline at end of file + self.assert_events(self.events, events) + + +class AnnotationsEvents(TracingEventsUnitTest): + def test_multiple_fun_annotations(self): + class AnnotationTracer: + def my_annotation(self, func=None, *, tag=None): + def decorator(f): + return f + return decorator + def your_annotation(self, func=None, *, tag=None): + def decorator(f): + return f + return decorator + annotation_tracer = AnnotationTracer() + + def func(): + @annotation_tracer.my_annotation(tag="outer") + @annotation_tracer.your_annotation(tag="inner") + def target(x, y): + pass + + def klass(): + @annotation_tracer.your_annotation(tag="outer") + @annotation_tracer.my_annotation(tag="inner") + class TargetCls: + pass + + self.trace_function(func) + events = [ + (0, 'func', 'call'), + (1, 'func', 'line'), + (-10, 'my_annotation', 'call'), + (-9, 'my_annotation', 'line'), + (-7, 'my_annotation', 'line'), + (-7, 'my_annotation', 'return'), + (2, 'func', 'line'), + (-6, 'your_annotation', 'call'), + (-5, 'your_annotation', 'line'), + (-3, 'your_annotation', 'line'), + (-3, 'your_annotation', 'return'), + (3, 'func', 'line'), + (2, 'func', 'line'), + (-5, 'decorator', 'call'), + (-4, 'decorator', 'line'), + (-4, 'decorator', 'return'), + (1, 'func', 'line'), + (-9, 'decorator', 'call'), + (-8, 'decorator', 'line'), + (-8, 'decorator', 'return'), + (3, 'func', 'line'), + (3, 'func', 'return'), + ] + self.assert_events(self.events, events) + + self.trace_function(klass) + events = [ + (0, 'klass', 'call'), + (1, 'klass', 'line'), + (-12, 'your_annotation', 'call'), + (-11, 'your_annotation', 'line'), + (-9, 'your_annotation', 'line'), + (-9, 'your_annotation', 'return'), + (2, 'klass', 'line'), + (-16, 'my_annotation', 'call'), + (-15, 'my_annotation', 'line'), + (-13, 'my_annotation', 'line'), + (-13, 'my_annotation', 'return'), + (3, 'klass', 'line'), + (1, 'TargetCls', 'call'), + (1, 'TargetCls', 'line'), + (4, 'TargetCls', 'line'), + (4, 'TargetCls', 'return'), + (2, 'klass', 'line'), + (-15, 'decorator', 'call'), + (-14, 'decorator', 'line'), + (-14, 'decorator', 'return'), + (1, 'klass', 'line'), + (-11, 'decorator', 'call'), + (-10, 'decorator', 'line'), + (-10, 'decorator', 'return'), + (3, 'klass', 'line'), + (3, 'klass', 'return'), + ] + self.assert_events(self.events, events) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py b/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py index fd9e01898e..a3fab2f075 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py @@ -1,4 +1,4 @@ -# 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 @@ -47,7 +47,12 @@ def test_sysconfig(): # must not fail -@unittest.skipIf(sys.implementation.name != 'graalpy', "GraalPy-only test") -def test_sysconfigdata(): - # Maturin loads this directly, make sure the import works - import _sysconfigdata +def test_platform_sysconfigdata(): + import importlib + import sysconfig + mod = importlib.import_module(sysconfig._get_sysconfigdata_name()) + # These flags are parsed directly from the file by maturin + for key in ("ABIFLAGS", "EXT_SUFFIX", "SOABI", "VERSION"): + assert key in mod.build_time_vars + assert mod.build_time_vars["ABIFLAGS"] == sys.abiflags + assert sys.abiflags in sysconfig.get_config_var("INCLUDEPY") diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_threading.py b/graalpython/com.oracle.graal.python.test/src/tests/test_threading.py index 854a14d7d2..c3401a8efc 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_threading.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_threading.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 @@ -37,9 +37,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import sys +import unittest + def test_stuck_thread(): - import sys import subprocess if sys.implementation.name == 'graalpy' and __graalpython__.posix_module_backend() == 'java': @@ -60,3 +62,55 @@ def test_stuck_thread(): ] ) assert not output, "No output expected" + + +def test_threads_joining_main_thread_at_shutdown(): + import subprocess + + script = r""" +import threading +import time + +def f(): + threading.main_thread().join() + +for _ in range(100): + threading.Thread(target=f).start() + +time.sleep(0.05) +""" + result = subprocess.run([sys.executable, "-c", script], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + assert result.returncode == 0, result.stderr.decode("utf-8", "replace") + + +@unittest.skipIf(sys.implementation.name == "graalpy", "Blocked on Truffle API support for blocking native reads during thread-local handshakes") +# see GR-75767 for details +def test_blocking_os_read_thread_does_not_deadlock_import_re(): + import subprocess + + if sys.platform == "win32": + return + + script = r""" +import os +import threading +import sys + +read_fd, write_fd = os.pipe() +threading.Thread(target=lambda: os.read(read_fd, 1), daemon=True).start() + +import re + +print("ok", flush=True) +os._exit(0) +""" + + result = subprocess.run( + [sys.executable, "-c", script], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=10, + ) + assert result.returncode == 0, result.stderr + assert result.stdout.rstrip().endswith("ok"), result.stdout diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py b/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py index c045b20bf9..6a79750950 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.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 @@ -36,9 +36,13 @@ # 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 os +import ast +import concurrent.futures +import linecache +import subprocess import sys -import unittest +import traceback +import threading def assert_raises(err, fn, *args, **kwargs): @@ -62,6 +66,34 @@ def test_traceback_dir(): assert set(dir(tb)) == {'tb_frame', 'tb_next', 'tb_lasti', 'tb_lineno'} +def test_traceback_frame_from_threadpool_exception_published_before_workitem_exit(): + published = threading.Event() + release = threading.Event() + original_set_exception = concurrent.futures.Future.set_exception + + def set_exception_and_wait(self, exc): + original_set_exception(self, exc) + published.set() + release.wait(timeout=5) + + def target(): + raise OSError("published before _WorkItem.run exits") + + concurrent.futures.Future.set_exception = set_exception_and_wait + try: + executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) + try: + future = executor.submit(target) + assert published.wait(timeout=60) + exc = future.exception(timeout=0) + traceback.clear_frames(exc.__traceback__) + finally: + release.set() + executor.shutdown(wait=True) + finally: + concurrent.futures.Future.set_exception = original_set_exception + + def test_import(): imported = True try: @@ -104,6 +136,32 @@ def test(): ) +def test_traceback_from_ast_without_end_positions(): + import traceback + + filename = "" + source = "def f():\n value = None\n raise AssertionError\n" + linecache.cache[filename] = (len(source), None, source.splitlines(True), filename) + tree = ast.parse(source, filename) + for node in ast.walk(tree): + if hasattr(node, "end_lineno"): + node.end_lineno = None + node.end_col_offset = None + + namespace = {} + exec(compile(tree, filename, "exec"), namespace) + try: + namespace["f"]() + except AssertionError: + stack = traceback.TracebackException(*sys.exc_info()).stack + else: + assert False, "generated function did not raise" + + assert stack[-1].name == "f" + assert stack[-1].lineno == 3 + assert stack[-1].line == "raise AssertionError" + + def test_basic_traceback_generator(): def foo(): yield 1 @@ -612,7 +670,7 @@ def test_faulthandler_many_threads(): for i in range(nthreads): loc = {} - src = f"def thread_func_{i}(evt, barrier):\n barrier.wait()\n evt.wait(5)\n" + src = f"def thread_func_{i}(evt, barrier):\n barrier.wait()\n evt.wait(30)\n" exec(src, {}, loc) target = loc[f"thread_func_{i}"] t = threading.Thread(target=target, args=(evt, barrier), name=f"thread-{i}") @@ -628,33 +686,74 @@ def test_faulthandler_many_threads(): t.join(timeout=2) assert False, f"Barrier wait failed: {e!r}" + header_re = re.compile(r'^Thread.+', re.MULTILINE) + func_name_re = re.compile(r'in (thread_func_(\d+))\b') + + def find_interleaved_block(out): + headers = list(header_re.finditer(out)) + for idx, m in enumerate(headers): + start = m.end() + end = headers[idx + 1].start() if idx + 1 < len(headers) else len(out) + ids = set(m.group(2) for m in func_name_re.finditer(out[start:end])) + if len(ids) > 1: + return m.group(), ids + return None + try: - f = tempfile.TemporaryFile() - faulthandler.dump_traceback(file=f, all_threads=True) - f.flush() - f.seek(0) - out = f.read().decode('utf-8', 'replace') + interleaved_block = None + for _ in range(3): + with tempfile.TemporaryFile() as f: + faulthandler.dump_traceback(file=f, all_threads=True) + f.flush() + f.seek(0) + out = f.read().decode('utf-8', 'replace') + interleaved_block = find_interleaved_block(out) + if interleaved_block is None: + break + assert interleaved_block is None, ( + f"Interleaved output detected in block {interleaved_block[0]!r} " + f"with multiple thread func ids: {interleaved_block[1]}" + ) finally: - try: - f.close() - except Exception: - pass evt.set() for t in threads: t.join(timeout=3) - header_re = re.compile(r'Thread.+') - headers = list(header_re.finditer(out)) - blocks = [] - for idx, m in enumerate(headers): - start = m.end() - end = headers[idx + 1].start() if idx + 1 < len(headers) else len(out) - blocks.append((m.group(), out[start:end])) - func_name_re = re.compile(r'in (thread_func_(\d+))\b') - ids_per_block = [] - for header, content in blocks: - ids = set(m.group(2) for m in func_name_re.finditer(content)) - if ids: - ids_per_block.append(ids) - assert len(ids) == 1, f"Interleaved output detected in block {header!r} with multiple thread func ids: {ids}" +def test_faulthandler_sigsegv_builtin(): + import faulthandler + + try: + if not __graalpython__.is_native or __graalpython__.posix_module_backend() == "java": + return + except NameError: + pass # CPython + + def assert_fatal_faulthandler_call(name, *args): + assert hasattr(faulthandler, name) + code = f"import faulthandler; faulthandler.{name}({', '.join(map(repr, args))})" + proc = subprocess.run( + [sys.executable, "-c", code], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + assert proc.returncode != 0 + assert proc.returncode != 1, ( + f"expected fatal signal path for faulthandler.{name}{args}, " + f"got regular Python error exit {proc.returncode}" + ) + + for release_gil in (False, True): + assert_fatal_faulthandler_call("_sigsegv", release_gil) + assert_fatal_faulthandler_call("_sigabrt") + + +def test_location_from_ast(): + m = compile("a = 1\nx", "", "exec", flags=ast.PyCF_ONLY_AST) + + try: + exec(compile(m, "", "exec")) + except NameError as e: + assert e.__traceback__.tb_next.tb_lineno == 2 + else: + assert False diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_tuple.py b/graalpython/com.oracle.graal.python.test/src/tests/test_tuple.py index 4f0094f8b5..4d41ba3a38 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_tuple.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_tuple.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2024, 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 @@ -283,6 +283,38 @@ def maketuple(t): self.assertTrue(tuple(b) is b) self.assertFalse(tuple(a) is a) + def test_constructor_validates_length_hint_before_iteration(self): + class BadLengthHint: + def __getitem__(self, index): + raise AssertionError("__getitem__ should not be called") + + def __length_hint__(self): + return None + + with self.assertRaisesRegex(TypeError, "__length_hint__ must be an integer"): + tuple(BadLengthHint()) + + def test_constructor_validates_len_before_iteration(self): + class BadLen: + def __getitem__(self, index): + raise AssertionError("__getitem__ should not be called") + + def __len__(self): + return -1 + + with self.assertRaisesRegex(ValueError, "__len__\\(\\) should return >= 0"): + tuple(BadLen()) + + def test_constructor_length_hint_too_small(self): + class SmallLengthHint: + def __iter__(self): + return iter((1, 2, 3)) + + def __length_hint__(self): + return 1 + + self.assertEqual(tuple(SmallLengthHint()), (1, 2, 3)) + class TupleCompareTest(CompareTest): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_type.py b/graalpython/com.oracle.graal.python.test/src/tests/test_type.py index c6b18a163f..324c9e1d30 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_type.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_type.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2022, 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 @@ -93,7 +93,7 @@ class BB (A): pass class B(BB): pass - class C (A): + class C (A): __slots__ = ['a'] class D (B, C): pass @@ -135,7 +135,7 @@ class C(A, B): pass assert C.__bases__ == (A, B) assert A.__subclasses__() == [C] - assert B.__subclasses__() == [C] + assert B.__subclasses__() == [C] #----------------------------------------------- @@ -191,6 +191,26 @@ def test_isinstance_non_type(): assert isinstance(1, typing.AbstractSet) is False +def test_object_new_with_generic_base_and_slots(): + from typing import Generic, TypeVar + + KT = TypeVar("KT") + VT = TypeVar("VT") + + class Foo(Generic[KT, VT]): + __slots__ = ("x", "y") + + def __new__(cls, x, y): + self = super(Foo, cls).__new__(cls) + self.x = x + self.y = y + return self + + foo = Foo(1, 2) + assert foo.x == 1 + assert foo.y == 2 + + @skipIf(sys.implementation.name == 'cpython' and sys.version_info[0:2] < (3, 8), "skipping for cPython versions < 3.8") def test_flags(): import functools @@ -264,39 +284,39 @@ def dict_element_raises(o, err): o['__dict__'] except err: raised = True - assert raised - + assert raised + class Base: pass - + class Sub(Base): pass - + str(type(Base.__dict__['__dict__'])) == "" - dict_element_raises(Sub.__dict__, KeyError) - Base().__dict__ == {} - Sub().__dict__ == {} + dict_element_raises(Sub.__dict__, KeyError) + Base().__dict__ == {} + Sub().__dict__ == {} class BaseSlots: __slots__ = ['a'] - - dict_element_raises(BaseSlots.__dict__, KeyError) + + dict_element_raises(BaseSlots.__dict__, KeyError) raised = False try: BaseSlots().__dict__ except AttributeError: raised = True assert raised - + class SubSlots(BaseSlots): pass str(type(SubSlots.__dict__['__dict__'])) == "" assert SubSlots().__dict__ == {} - + class SubSlots(BaseSlots, Base): pass - + str(type(SubSlots.__dict__['__dict__'])) == "" assert SubSlots().__dict__ == {} @@ -306,21 +326,21 @@ def test_itemsize(): assert list.__itemsize__ == 0 assert type.__itemsize__ == 40 assert tuple.__itemsize__ == 8 - + class C: pass assert C.__itemsize__ == 0 - + class C(tuple): pass assert C.__itemsize__ == 8 - - + + raised = False try: object.__itemsize__ = 1 except TypeError: raised = True assert raised - + raised = False try: C.__itemsize__ = 1 @@ -331,13 +351,13 @@ class C(tuple): pass class C(): __itemsize__ = 'abc' assert C.__itemsize__ == 0 - + class C(tuple): __itemsize__ = 42 assert C.__itemsize__ == 8 -def test_descr_name_qualname(): +def test_descr_name_qualname(): assert float.real.__qualname__ == 'float.real' assert float.real.__name__ == 'real' class C: __slots__ = ['a'] @@ -348,13 +368,13 @@ class C: __slots__ = ['a'] C.a.__qualname__ = 'b' except AttributeError: raised = True - assert raised + assert raised raised = False try: C.a.__name__ = 'b' except AttributeError: raised = True - assert raised + assert raised def test_cant_set_builtin_attributes(): @@ -364,41 +384,41 @@ def test_cant_set_builtin_attributes(): except TypeError: raised = True assert raised - + raised = False try: float.__qualname__ = 'b' except TypeError: raised = True assert raised - + raised = False try: float.__dictoffset__ = 'b' except TypeError: raised = True assert raised - + raised = False try: float.__module__ = 'b' except TypeError: raised = True assert raised - + raised = False try: float.__itemsize__ = 'b' except TypeError: raised = True assert raised - + raised = False try: float.__basicsize__ = 'b' except TypeError: raised = True - assert raised + assert raised def test_slots_no_instance_layout_conflict(): diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_unicodedata.py b/graalpython/com.oracle.graal.python.test/src/tests/test_unicodedata.py index 4fecd2c658..410c25f0f2 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_unicodedata.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_unicodedata.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 @@ -75,6 +75,20 @@ def test_lookup(self): with self.assertRaisesRegex(KeyError, "name too long"): unicodedata.lookup("a" * 257) + def test_lookup_unknown_name_via_fallback(self): + with self.assertRaisesRegex(KeyError, "undefined character name 'GIBBET NICH'"): + unicodedata.lookup("GIBBET NICH") + + def test_lookup_named_sequence(self): + if unicodedata.ucd_3_2_0.bidirectional == unicodedata.bidirectional: + raise unittest.SkipTest("Only supported with CPython's unicodedata.ucd_3_2_0") + + unicode_name = "LATIN SMALL LETTER R WITH TILDE" + self.assertEqual(unicodedata.lookup(unicode_name), "\u0072\u0303") + + with self.assertRaisesRegex(KeyError, "undefined character name 'LATIN SMALL LETTER R WITH TILDE'"): + unicodedata.ucd_3_2_0.lookup(unicode_name) + def test_east_asian_width(self): list = [1, 2, 3] @@ -101,4 +115,4 @@ def test_combining(self): empty_string = "" with self.assertRaisesRegex(TypeError, r"combining\(\) argument must be a unicode character, not str"): - unicodedata.combining(empty_string) \ No newline at end of file + unicodedata.combining(empty_string) 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 87a4394767..539d5d8d53 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 @@ -1,4 +1,4 @@ -# 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 @@ -42,8 +42,11 @@ import subprocess import sys import tempfile +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' @@ -66,22 +69,98 @@ def test_venv_launcher(self): import struct with tempfile.TemporaryDirectory() as d: tmpfile = os.path.join(d, "venvlauncher.exe") + launcher_command = f'"{os.path.realpath(sys.executable)}" -S' shutil.copy(os.path.join(venv.__path__[0], "scripts", "nt", "graalpy.exe"), tmpfile) with open(tmpfile, "ab") as f: - sz = f.write(sys.executable.encode("utf-16le")) + sz = f.write(launcher_command.encode("utf-16le")) assert f.write(struct.pack("@I", sz)) == 4 + env = os.environ.copy() + env["PYLAUNCHER_DEBUG"] = "1" try: out = subprocess.check_output([tmpfile, "-c", """if True: import sys, os x = os print("Hello", sys.executable) + print("Base", sys._base_executable) print("Original", __graalpython__.venvlauncher_command) - """], env={"PYLAUNCHER_DEBUG": "1"}, text=True) + """], env=env, stderr=subprocess.STDOUT, text=True) except subprocess.CalledProcessError as err: - out = err.output.decode(errors="replace") if err.output else "" + if isinstance(err.output, str): + out = err.output + else: + out = err.output.decode(errors="replace") if err.output else "" print("out=", out, sep="\n") assert f"Hello {tmpfile}" in out, out - assert f'Original "{sys.executable}"' in out, out + assert f"Base {os.path.realpath(sys.executable)}" in out, out + assert f'Original {launcher_command}' in out, out + + def test_nested_windows_venv_preserves_base_executable(self): + if sys.platform != "win32" or sys.implementation.name != "graalpy": + return + expected_base = os.path.realpath(getattr(sys, "_base_executable", sys.executable)) + with tempfile.TemporaryDirectory() as outer_dir, tempfile.TemporaryDirectory() as inner_root: + inner_dir = os.path.join(inner_root, "inner") + extra_args = [ + f'--vm.Dpython.EnableBytecodeDSLInterpreter={repr(__graalpython__.is_bytecode_dsl_interpreter).lower()}' + ] + subprocess.check_output([sys.executable] + extra_args + ["-m", "venv", outer_dir, "--without-pip"], stderr=subprocess.STDOUT) + outer_python = os.path.join(outer_dir, BINDIR, f"python{EXESUF}") + out = subprocess.check_output([ + outer_python, + "-c", + textwrap.dedent(f""" + import os + import sys + import venv + + inner_dir = {inner_dir!r} + venv.EnvBuilder(with_pip=False).create(inner_dir) + print("OUTER_BASE", os.path.realpath(sys._base_executable)) + with open(os.path.join(inner_dir, "pyvenv.cfg"), encoding="utf-8") as cfg: + print(cfg.read()) + """) + ], text=True) + assert f"OUTER_BASE {expected_base}" in out, out + assert f"base-executable = {expected_base}" in out, out + + def test_macos_venv_launcher_with_space_in_command_path(self): + if sys.platform != "darwin" or sys.implementation.name != "graalpy": + return + import venv + real_executable = os.path.realpath(sys.executable) + real_home = os.path.dirname(os.path.dirname(real_executable)) + launcher_template = os.path.join(venv.__path__[0], "scripts", "macos", "graalpy") + assert os.path.exists(launcher_template), launcher_template + with tempfile.TemporaryDirectory(prefix="graalpy launcher ") as d: + linked_home = os.path.join(d, "home with space") + os.symlink(real_home, linked_home) + linked_executable = os.path.join(linked_home, "bin", os.path.basename(real_executable)) + env_dir = os.path.join(d, "venv") + bin_dir = os.path.join(env_dir, BINDIR) + os.makedirs(bin_dir) + env_launcher = os.path.join(bin_dir, "graalpy") + shutil.copyfile(launcher_template, env_launcher) + os.chmod(env_launcher, 0o755) + with open(os.path.join(env_dir, "pyvenv.cfg"), "w", encoding="utf-8") as cfg: + cfg.write(f"venvlauncher_command = {linked_executable}\n") + with open(os.path.join(env_dir, "pyvenv.cfg"), encoding="utf-8") as cfg: + cfg_data = cfg.read() + assert f"venvlauncher_command = {linked_executable}" in cfg_data, cfg_data + out = subprocess.check_output( + [ + env_launcher, + "-c", + """if True: + import os, sys + print("Executable", os.path.realpath(sys.executable)) + print("Original", __graalpython__.venvlauncher_command) + """, + ], + stderr=subprocess.STDOUT, + text=True, + ) + assert f"Executable {os.path.realpath(env_launcher)}" in out, out + assert f'Original "{linked_executable}"' in out, out def test_create_and_use_basic_venv(self): run = None @@ -101,6 +180,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_weakref.py b/graalpython/com.oracle.graal.python.test/src/tests/test_weakref.py index e47a8975d4..987ef52658 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_weakref.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_weakref.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 @@ -92,7 +92,7 @@ def do_hash(item): # try hard to collect the two objects but avoid infinite loop i = 0 - while not (r1() is None and r3() is None) and i < 10000: + while not (r1() is None and r3() is None) and i < 100: gc.collect() i += 1 @@ -128,6 +128,34 @@ def test_weakref_object_type_support(): assert False, "should throw TypeError for unsupported objects" +def test_proxy_does_not_keep_object_alive(): + import gc + + class A: + attr = 42 + + a = A() + proxy = weakref.proxy(a) + ref = weakref.ref(a) + + assert proxy.attr == 42 + a = None + + for _ in range(10000): + gc.collect() + if ref() is None: + break + + assert ref() is None + assert "NoneType" in repr(proxy) + try: + getattr(proxy, "attr") + except ReferenceError: + pass + else: + assert False, "weakref proxy kept the object alive" + + def test_proxy_getitem(): class A: def __init__(self, collection): @@ -293,4 +321,4 @@ def __call__(self): a = A(42) proxy = weakref.proxy(a) assert callable(proxy) is True - assert proxy() == 42 \ No newline at end of file + assert proxy() == 42 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/test_xml.py b/graalpython/com.oracle.graal.python.test/src/tests/test_xml.py new file mode 100644 index 0000000000..bfb0288327 --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_xml.py @@ -0,0 +1,76 @@ +# 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. + +import unittest +from xml.parsers import expat + + +class PyExpatXmlTest(unittest.TestCase): + + def test_xml_decl_handler_not_called_without_xml_declaration(self): + parser = expat.ParserCreate() + xml_decl_calls = [] + + parser.XmlDeclHandler = lambda *args: xml_decl_calls.append(args) + parser.Parse(b"", True) + + self.assertEqual([], xml_decl_calls) + + def test_parser_create_encoding_is_used_for_byte_input(self): + parser = expat.ParserCreate(encoding="iso-8859-1") + character_data = [] + + parser.CharacterDataHandler = character_data.append + parser.Parse(b"\xe9", True) + + self.assertEqual(["\xe9"], character_data) + + def test_external_doctype_reports_no_internal_subset(self): + parser = expat.ParserCreate() + doctype_calls = [] + + parser.StartDoctypeDeclHandler = lambda *args: doctype_calls.append(args) + parser.ExternalEntityRefHandler = lambda *args: 1 + parser.Parse(b'', True) + + self.assertEqual([("doc", "x.dtd", None, 0)], doctype_calls) + + +if __name__ == '__main__': + unittest.main() diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_zlib.py b/graalpython/com.oracle.graal.python.test/src/tests/test_zlib.py index 0cab54eaac..f9331de980 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_zlib.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_zlib.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 @@ -9,6 +9,8 @@ import unittest import zlib +from tests.util import assert_raises + pintNumber = 98765432109876543210 longNumber = 9876543210 GZ_FILE_NAME = 'testgzfile.gz' @@ -242,6 +244,119 @@ def test_zlib_decompress_gzip(): data = d.decompress(f.read()) + d.flush() assert data == GZ_DATA + +def test_zlib_decompress_gzip_bad_trailer(): + import gzip + + compressed = bytearray(gzip.compress(GZ_DATA)) + compressed[-8] ^= 1 + assert_raises(zlib.error, zlib.decompress, compressed, 16 + zlib.MAX_WBITS) + + compressed = bytearray(gzip.compress(GZ_DATA)) + compressed[-4] ^= 1 + d = zlib.decompressobj(16 + zlib.MAX_WBITS) + + def decompress_streaming(): + return d.decompress(compressed[:20]) + d.decompress(compressed[20:]) + d.flush() + + assert_raises(zlib.error, decompress_streaming) + + +def gzip_bytes_with_header_crc(data, header_crc=None): + import struct + + compressor = zlib.compressobj(wbits=-zlib.MAX_WBITS) + compressed = compressor.compress(data) + compressor.flush() + header = b"\x1f\x8b\x08\x02\x00\x00\x00\x00\x02\xff" + if header_crc is None: + header_crc = zlib.crc32(header) & 0xffff + trailer = struct.pack(" zlib.DEF_BUF_SIZE + + copied = decompressor.copy() + assert first + copied.decompress(compressed[400:]) + copied.flush() == contents + + +def test_zlib_decompress_gzip_copy_after_eof_consumes_trailer(): + import gzip + + compressed = gzip.compress(b"x") + decompressor = zlib.decompressobj(16 + zlib.MAX_WBITS) + assert decompressor.decompress(compressed) == b"x" + assert decompressor.eof + + copied = decompressor.copy() + assert copied.eof + assert copied.decompress(b"") == b"" + + +def test_zlib_decompress_copy_preserves_eof_after_max_length(): + compressed = zlib.compress(b"abc") + decompressor = zlib.decompressobj() + assert decompressor.decompress(compressed, 1) == b"a" + assert not decompressor.eof + + copied = decompressor.copy() + assert not copied.eof + + +def test_zlib_decompress_copy_preserves_consumed_input_without_output(): + compressed = zlib.compress(HAMLET_SCENE) + decompressor = zlib.decompressobj() + first = decompressor.decompress(compressed[:32]) + assert first == b"" + + copied = decompressor.copy() + assert copied.decompress(compressed[32:]) + copied.flush() == HAMLET_SCENE + + def test_GR65704(): contents = b"The quick brown fox jumped over the lazy dog" wbits = 27 @@ -280,3 +395,34 @@ def test_various_chunks(): decompressed += decompressor.decompress(compressed[200:]) assert decompressed == contents + +def test_gzip_decompress_max_length_unconsumed_tail(): + import gzip + + contents = bytes(range(251)) * 100 + compressed = gzip.compress(contents) + decompressor = zlib.decompressobj(16 + zlib.MAX_WBITS) + + decompressed = b'' + for i in range(0, len(compressed), 8192): + decompressed += decompressor.decompress(compressed[i:i + 8192], 1) + while decompressor.unconsumed_tail: + decompressed += decompressor.decompress(decompressor.unconsumed_tail, 1) + decompressed += decompressor.flush() + + assert decompressed == contents + assert decompressor.eof + assert decompressor.unused_data == b'' + + +def test_gzip_decompress_post_eof_unused_data(): + import gzip + + compressed = gzip.compress(GZ_DATA) + decompressor = zlib.decompressobj(16 + zlib.MAX_WBITS) + + assert decompressor.decompress(compressed + b'first') == GZ_DATA + assert decompressor.eof + assert decompressor.unused_data == b'first' + assert decompressor.decompress(b'second') == b'' + assert decompressor.unused_data == b'firstsecond' diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_abstract_numbers.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_abstract_numbers.txt index 448d6cce0c..fe81d3ff19 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_abstract_numbers.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_abstract_numbers.txt @@ -1,7 +1,7 @@ -test.test_abstract_numbers.TestNumbers.test_complex @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_abstract_numbers.TestNumbers.test_float @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_abstract_numbers.TestNumbers.test_int @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_abstract_numbers.TestNumbersDefaultMethods.test_complex @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_abstract_numbers.TestNumbersDefaultMethods.test_integral @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_abstract_numbers.TestNumbersDefaultMethods.test_rational @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_abstract_numbers.TestNumbersDefaultMethods.test_real @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_abstract_numbers.TestNumbers.test_complex @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_abstract_numbers.TestNumbers.test_float @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_abstract_numbers.TestNumbers.test_int @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_abstract_numbers.TestNumbersDefaultMethods.test_complex @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_abstract_numbers.TestNumbersDefaultMethods.test_integral @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_abstract_numbers.TestNumbersDefaultMethods.test_rational @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_abstract_numbers.TestNumbersDefaultMethods.test_real @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_asyncgen.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_asyncgen.txt index 72c22c090e..eee992af6c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_asyncgen.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_asyncgen.txt @@ -1,38 +1,38 @@ -test.test_asyncgen.AsyncGenAsyncioTest.test_aiter_bad_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_aiter_idempotent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_anext_bad_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_anext_return_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_anext_return_iterator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aclose_after_exhaustion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aclose_compatible_with_get_stack @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aclose_twice_with_different_coros @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aiter_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_aclose_10 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_aclose_11 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_04 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_05 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_06 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_stopiteration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_asend_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_asend_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_asend_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_stopiteration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_shutdown_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_shutdown_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_shutdown_exception_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_await_same_aclose_coro_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_await_same_anext_coro_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_expression_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncgen.AsyncGenAsyncioTest.test_asyncgen_nonstarted_hooks_are_cancellable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncgen.AsyncGenAsyncioTest.test_aiter_bad_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_aiter_idempotent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_anext_bad_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_anext_return_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_anext_return_iterator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aclose_after_exhaustion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aclose_compatible_with_get_stack @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aclose_twice_with_different_coros @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_aiter_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_aclose_10 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_aclose_11 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_04 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_05 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_06 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_stopiteration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_anext_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_asend_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_asend_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_asend_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_stopiteration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_athrow_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_shutdown_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_shutdown_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_shutdown_exception_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_await_same_aclose_coro_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_await_same_anext_coro_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_expression_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncgen.AsyncGenAsyncioTest.test_asyncgen_nonstarted_hooks_are_cancellable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncgen.AsyncGenSyntaxTest.test_async_gen_syntax_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_asyncgen.AsyncGenSyntaxTest.test_async_gen_syntax_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_asyncgen.AsyncGenSyntaxTest.test_async_gen_syntax_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_asyncio.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_asyncio.txt index 57ba7ef4ad..68858730a4 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_asyncio.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_asyncio.txt @@ -1,191 +1,207 @@ -test.test_asyncio.test_base_events.BaseEventLoopTests.test__add_callback_cancelled_handle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test__add_callback_handle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test__run_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test__run_once_cancelled_event_cleanup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test__run_once_schedule_handle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_call_later @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_call_later_negative_delays @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_call_soon @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_call_soon_non_callable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_check_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_create_named_task_with_custom_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_create_named_task_with_default_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_create_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_create_task_error_closes_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_default_exc_handler_broken @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_default_exc_handler_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_forever_keyboard_interrupt @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_forever_pre_stopped @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_until_complete_baseexception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_until_complete_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_until_complete_loop_orphan_future_close_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_until_complete_type_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_default_executor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_default_executor_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_exc_handler_broken @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_exc_handler_custom @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_exc_handler_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_task_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_task_factory_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_shutdown_default_executor_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_single_selecter_event_callback_after_stopping @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_subprocess_exec_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_subprocess_shell_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopTests.test_time_and_call_at @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_accept_connection_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_accept_connection_retry @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_call_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_bluetooth @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_connect_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_host_port_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_ip_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_multiple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_multiple_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_multiple_errors_local_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_getaddrinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_host_port_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_base_events.BaseEventLoopTests.test__add_callback_cancelled_handle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test__add_callback_handle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test__run_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test__run_once_cancelled_event_cleanup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test__run_once_schedule_handle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_call_later @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_call_later_negative_delays @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_call_soon @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_call_soon_non_callable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_check_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_create_named_task_with_custom_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_create_named_task_with_default_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_create_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_create_task_error_closes_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_default_exc_handler_broken @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_default_exc_handler_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_forever_keyboard_interrupt @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_forever_pre_stopped @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_until_complete_baseexception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_until_complete_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_until_complete_loop_orphan_future_close_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_run_until_complete_type_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_default_executor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_default_executor_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_exc_handler_broken @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_exc_handler_custom @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_exc_handler_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_task_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_set_task_factory_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_shutdown_default_executor_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_single_selecter_event_callback_after_stopping @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_subprocess_exec_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_subprocess_shell_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopTests.test_time_and_call_at @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_accept_connection_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_accept_connection_retry @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_call_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_bluetooth @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_connect_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_host_port_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_ip_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_multiple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_multiple_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_multiple_errors_local_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_getaddrinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_host_port_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_inet_pton @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_local_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_ssl_server_hostname_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_local_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_no_ssl_server_hostname_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_service_name @ darwin-arm64,linux-aarch64,linux-x86_64 # GR-73063 !test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_service_name @ linux-aarch64-github,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_ssl_server_hostname_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_ssl_server_hostname_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_ssl_timeout_for_plain_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_wrong_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_addr_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_allow_broadcast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_cant_bind @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_connect_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_ip_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_no_addrinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_no_matching_family @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_noaddr_nofamily @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_nosoreuseport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_setblk_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_sock_sockopts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_socket_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_ssl_server_hostname_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_ssl_server_hostname_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_ssl_timeout_for_plain_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_connection_wrong_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_addr_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_allow_broadcast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_cant_bind @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_connect_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_ip_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_no_addrinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_no_matching_family @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_noaddr_nofamily @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_nosoreuseport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_setblk_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_sock_sockopts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_socket_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_sockopts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_wrong_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_cant_bind @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_empty_host @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_host_port_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_datagram_endpoint_wrong_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_cant_bind @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_empty_host @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_host_port_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_ipv6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_no_getaddrinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_no_host_port_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_nosoreuseport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_soreuseport_only_defined @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_ssl_timeout_for_plain_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_wrong_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_getnameinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventTests.test_ipaddr_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventTests.test_ipaddr_info_no_inet_pton @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseEventTests.test_port_parameter_types @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test__sock_sendfile_native_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_blocking_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_negative_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_negative_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_nonbinary_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_nonstream_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_notint_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_notint_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_sock_sendfile_fallback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_sock_sendfile_fallback_offset_and_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_sock_sendfile_no_fallback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_base_events.RunningLoopTests.test_running_loop_within_a_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_no_getaddrinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_no_host_port_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_nosoreuseport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_soreuseport_only_defined @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_ssl_timeout_for_plain_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_create_server_wrong_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventLoopWithSelectorTests.test_getnameinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventTests.test_ipaddr_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventTests.test_ipaddr_info_no_inet_pton @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseEventTests.test_port_parameter_types @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test__sock_sendfile_native_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_blocking_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_negative_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_negative_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_nonbinary_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_nonstream_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_notint_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_notint_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_sock_sendfile_fallback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_sock_sendfile_fallback_offset_and_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.BaseLoopSockSendfileTests.test_sock_sendfile_no_fallback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_base_events.RunningLoopTests.test_running_loop_within_a_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_base_events.TestSelectorUtils.test_set_nodelay @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_buffered_proto.BufferedProtocolSelectorTests.test_buffered_proto_create_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_context.DecimalContextTest.test_asyncio_task_decimal_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.EagerPyTaskTests.test_awaitables_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.EagerPyTaskTests.test_recursive_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.EagerPyTaskTests.test_recursive_taskgroups @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.EagerTests.test_awaitables_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.EagerTests.test_recursive_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.EagerTests.test_recursive_taskgroups @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.NonEagerPyTaskTests.test_awaitables_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.NonEagerPyTaskTests.test_recursive_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.NonEagerPyTaskTests.test_recursive_taskgroups @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.NonEagerTests.test_awaitables_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.NonEagerTests.test_recursive_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.NonEagerTests.test_recursive_taskgroups @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_all_tasks_with_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_all_tasks_with_eager_completion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_await_future_during_eager_step @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_block_after_eager_step @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_cancellation_after_eager_completion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_cancellation_after_eager_step_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_context_vars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_eager_completion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_eager_task_factory_set @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_staggered_race_with_eager_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_staggered_race_with_eager_tasks_no_delay @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.AbstractEventLoopTests.test_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.AbstractEventLoopTests.test_not_implemented_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.HandleTests.test_callback_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.HandleTests.test_coroutine_like_object_debug_formatting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.HandleTests.test_handle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.HandleTests.test_handle_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.HandleTests.test_handle_repr_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.HandleTests.test_handle_source_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.HandleTests.test_handle_weakref @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_event_loop_policy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_get_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_get_event_loop_after_set_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_get_event_loop_calls_set_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_get_event_loop_policy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_get_event_loop_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_new_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_set_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.PolicyTests.test_set_event_loop_policy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_add_fds_after_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_buffered_proto.BufferedProtocolSelectorTests.test_buffered_proto_create_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_context.DecimalContextTest.test_asyncio_task_decimal_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.EagerPyTaskTests.test_awaitables_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.EagerPyTaskTests.test_recursive_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.EagerPyTaskTests.test_recursive_taskgroups @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.EagerTests.test_awaitables_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.EagerTests.test_recursive_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.EagerTests.test_recursive_taskgroups @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.NonEagerPyTaskTests.test_awaitables_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.NonEagerPyTaskTests.test_recursive_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.NonEagerPyTaskTests.test_recursive_taskgroups @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.NonEagerTests.test_awaitables_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.NonEagerTests.test_recursive_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.NonEagerTests.test_recursive_taskgroups @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_all_tasks_with_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_all_tasks_with_eager_completion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_await_future_during_eager_step @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_block_after_eager_step @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_cancellation_after_eager_completion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_cancellation_after_eager_step_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_context_vars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_eager_completion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_eager_task_factory_set @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_staggered_race_with_eager_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_eager_task_factory.PyEagerTaskFactoryLoopTests.test_staggered_race_with_eager_tasks_no_delay @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.AbstractEventLoopTests.test_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.AbstractEventLoopTests.test_not_implemented_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.HandleTests.test_callback_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.HandleTests.test_coroutine_like_object_debug_formatting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.HandleTests.test_handle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.HandleTests.test_handle_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.HandleTests.test_handle_repr_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.HandleTests.test_handle_source_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.HandleTests.test_handle_weakref @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_event_loop_policy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_get_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_get_event_loop_after_set_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_get_event_loop_calls_set_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_get_event_loop_policy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_get_event_loop_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_new_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_set_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.PolicyTests.test_set_event_loop_policy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_add_fds_after_closing @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_call_later @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_call_soon @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_call_soon_threadsafe @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_call_soon_threadsafe_same_thread @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_close @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_close_running_event_loop @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_connect_accepted_socket_ssl_timeout_for_plain_socket @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_create_datagram_endpoint_sock @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_create_server_multiple_hosts_ipv4 @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_create_server_multiple_hosts_ipv6 @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_run_until_complete @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_run_until_complete_stopped @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_subprocess_exec_invalid_args @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_subprocess_shell_invalid_args @ win32-AMD64 +test.test_asyncio.test_events.ProactorEventLoopTests.test_timeout_rounding @ win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_add_fds_after_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.SelectEventLoopTests.test_bidirectional_pty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_call_later @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_call_soon @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_call_soon_threadsafe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_call_soon_threadsafe_same_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_close_running_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_connect_accepted_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_connect_accepted_socket_ssl_timeout_for_plain_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_call_later @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_call_soon @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_call_soon_threadsafe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_call_soon_threadsafe_same_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_close_running_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_connect_accepted_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_connect_accepted_socket_ssl_timeout_for_plain_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.SelectEventLoopTests.test_create_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_create_connection_local_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_create_connection_local_addr_in_use @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_create_connection_local_addr_nomatch_family @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_create_connection_local_addr_skip_different_family @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_datagram_endpoint @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_datagram_endpoint_ipv6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_datagram_endpoint_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_server @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_create_datagram_endpoint @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_create_datagram_endpoint_ipv6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_create_datagram_endpoint_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_create_server @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_addr_in_use @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_dual_stack @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_multiple_hosts_ipv4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_multiple_hosts_ipv6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_multiple_hosts_ipv4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_multiple_hosts_ipv6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_reuse_port @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_ssl_verified @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_ssl_verify_failed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_ssl_verified @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_ssl_verify_failed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.SelectEventLoopTests.test_create_server_trsock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_datagram_send_to_non_listening_address @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_internal_fds @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_prompt_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_datagram_send_to_non_listening_address @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_internal_fds @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_prompt_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.SelectEventLoopTests.test_read_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_read_pty_output @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_reader_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_remove_fds_after_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_run_in_executor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_run_in_executor_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_run_until_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_run_until_complete_stopped @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_reader_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_remove_fds_after_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_run_in_executor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_run_in_executor_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_run_until_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.SelectEventLoopTests.test_run_until_complete_stopped @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.SelectEventLoopTests.test_server_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_ssl_connect_accepted_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_ssl_connect_accepted_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 # Timeouts !test.test_asyncio.test_events.SelectEventLoopTests.test_subprocess_close_after_finish test.test_asyncio.test_events.SelectEventLoopTests.test_subprocess_exec_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -195,434 +211,436 @@ test.test_asyncio.test_events.SelectEventLoopTests.test_subprocess_exec_invalid_ test.test_asyncio.test_events.SelectEventLoopTests.test_subprocess_shell_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github # Timeouts !test.test_asyncio.test_events.SelectEventLoopTests.test_subprocess_wait_no_same_group -test.test_asyncio.test_events.SelectEventLoopTests.test_timeout_rounding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_timeout_rounding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.SelectEventLoopTests.test_unclosed_pipe_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_write_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_write_pipe_disconnect_on_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_events.SelectEventLoopTests.test_write_pty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.SelectEventLoopTests.test_writer_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TestAbstractServer.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TestAbstractServer.test_get_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TestAbstractServer.test_wait_closed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.SelectEventLoopTests.test_writer_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TestAbstractServer.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TestAbstractServer.test_get_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TestAbstractServer.test_wait_closed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.TestCGetEventLoop.test_get_event_loop_new_process @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TestCGetEventLoop.test_get_event_loop_returns_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TestCGetEventLoop.test_get_event_loop_returns_running_loop2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.TestCGetEventLoop.test_get_event_loop_returns_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TestCGetEventLoop.test_get_event_loop_returns_running_loop2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_events.TestPyGetEventLoop.test_get_event_loop_new_process @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TestPyGetEventLoop.test_get_event_loop_returns_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TestPyGetEventLoop.test_get_event_loop_returns_running_loop2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TestServer.test_get_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TimerTests.test_hash @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TimerTests.test_timer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TimerTests.test_timer_comparison @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TimerTests.test_timer_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TimerTests.test_timer_repr_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_events.TimerTests.test_when @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.DuckTests.test_ensure_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.DuckTests.test_wrap_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_invoked_on_set_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_invoked_on_set_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_remove_first_and_second_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_remove_first_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_remove_third_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_evil_call_soon_list_mutation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_remove_done_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_remove_done_callbacks_list_clear @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_remove_done_callbacks_list_mutation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_schedule_callbacks_list_mutation_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_schedule_callbacks_list_mutation_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_use_after_free_on_fut_callback_0_with_evil__eq__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_use_after_free_on_fut_callback_0_with_evil__getattribute__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_use_after_free_on_fut_context_0_with_evil__getattribute__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureInheritanceTests.test_inherit_without_calling_super_init @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_constructor_positional @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_constructor_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_constructor_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_constructor_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_copy_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_exception_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_cancel_message_getter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_cancel_message_setter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_cancelled_exception_refcycles @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_cancelled_result_refcycles @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_del_collect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_iter_throw @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_source_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_future_stop_iteration_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_generic_alias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_initial_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_isfuture @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_iter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_log_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_set_result_unless_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_stop_iteration_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_stop_iteration_subclass_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_abandoned @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_exception_result_retrieved @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_exception_retrieved @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_not_called_after_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_result_retrieved @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_result_unretrieved @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_uninitialized @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_wrap_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_cancel2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures.PyFutureTests.test_yield_from_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures2.FutureReprTests.test_recursive_repr_for_pending_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures2.PyFutureTests.test_future_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_futures2.PyFutureTests.test_handle_exc_handler_correct_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_abort_barrier @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_abort_barrier_when_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_abort_barrier_when_exception_then_resetting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_abort_barrier_when_tasks_half_draining_half_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_barrier @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_barrier_parties @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_blocking_tasks_while_draining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_draining_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_filling_one_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_filling_one_task_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_filling_task_by_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_filling_tasks_cancel_one @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_filling_tasks_check_return_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_filling_tasks_wait_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_reset_barrier @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_when_tasks_half_draining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_when_tasks_half_draining_half_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_while_tasks_draining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_while_tasks_waiting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_while_tasks_waiting_and_waiting_again @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_ambiguous_loops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_explicit_lock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_notify @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_notify_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_notify_all_unacquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_notify_unacquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_timeout_in_block @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_wait_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_wait_cancel_after_notify @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_wait_cancel_contested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_wait_for_unacquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.ConditionTests.test_wait_unacquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.EventTests.test_clear @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.EventTests.test_clear_with_waiters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.EventTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.EventTests.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.EventTests.test_wait_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.EventTests.test_wait_on_set @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_acquire @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_acquire_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_cancel_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_cancel_release_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_finished_waiter_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_lock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_lock_by_with_statement @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_lock_doesnt_accept_loop_parameter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_release_no_waiters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_release_not_acquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.LockTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_acquire @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_acquire_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_acquire_cancel_before_awoken @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_acquire_fifo_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_acquire_fifo_order_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_acquire_fifo_order_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_acquire_hang @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_acquire_no_hang @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_initial_value_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_release_no_waiters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_release_not_acquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_locks.SemaphoreTests.test_semaphore_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.CoroutineTests.test_async_def_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.CoroutineTests.test_double_await @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.CoroutineTests.test_iscoroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.CoroutineTests.test_iscoroutine_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.CoroutineTests.test_iscoroutinefunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.CoroutineTests.test_task_print_stack @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.CoroutineTests.test_types_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.LockTests.test_context_manager_async_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.LockTests.test_context_manager_with_await @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_pep492.StreamReaderTests.test_readline @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_close_self_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_create_server @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_create_server_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_reading_aborted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_reading_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_reading_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_writing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_writing_aborted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_loop_self_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_loop_self_reading_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_loop_self_reading_fut @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_make_datagram_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_make_socket_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_process_events @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_stop_serving @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_write_to_self @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test__loop_writing_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test__loop_writing_error_received @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test__loop_writing_error_received_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test__loop_writing_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_fatal_error_connected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_buffer_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_buffer_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_connected_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_error_received @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_error_received_connected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_abort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_call_connection_lost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close_invalid_sockobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close_protocol_connection_lost_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close_write_fut @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_dont_pause_writing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_fatal_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_fatal_error_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_force_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_force_close_idempotent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_force_close_protocol_connection_lost_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_aborted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_aborted_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_aborted_is_fatal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_conn_reset_lost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing_force_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing_stop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_reading_connection_made @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_resume_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_resume_writing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_writing_2write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_writing_3write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof_buffer_write_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof_duplex_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof_write_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_more @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_protocols.ProtocolsAbsTests.test_base_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_protocols.ProtocolsAbsTests.test_buffered_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_protocols.ProtocolsAbsTests.test_datagram_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_protocols.ProtocolsAbsTests.test_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_protocols.ProtocolsAbsTests.test_subprocess_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.LifoQueueJoinTests.test_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.LifoQueueJoinTests.test_join_empty_queue @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.LifoQueueJoinTests.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.LifoQueueJoinTests.test_task_done_underflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.LifoQueueTests.test_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.PriorityQueueJoinTests.test_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.PriorityQueueJoinTests.test_join_empty_queue @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.PriorityQueueJoinTests.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.PriorityQueueJoinTests.test_task_done_underflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.PriorityQueueTests.test_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueBasicTests.test_empty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueBasicTests.test_full @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueBasicTests.test_generic_alias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueBasicTests.test_maxsize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueBasicTests.test_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueBasicTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueBasicTests.test_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_blocking_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_blocking_get_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_cancelled_getters_not_being_held_in_self_getters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_get_cancelled_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_get_with_putters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_get_with_waiting_putters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_nonblocking_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_nonblocking_get_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueGetTests.test_why_are_getters_waiting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueJoinTests.test_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueJoinTests.test_join_empty_queue @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueJoinTests.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueueJoinTests.test_task_done_underflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_blocking_put @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_blocking_put_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_cancelled_put_silence_value_error_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_cancelled_puts_not_being_held_in_self_putters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_float_maxsize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_get_cancel_drop_many_pending_readers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_get_cancel_drop_one_pending_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_nonblocking_put @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_nonblocking_put_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_put_cancel_drop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_put_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_put_cancelled_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_put_with_waiting_getters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_queues.QueuePutTests.test_why_are_putters_waiting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_cancels_hanging_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_closes_gens_after_hanging_tasks_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_from_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_loop_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_only_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_raises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_reports_hanging_tasks_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_return @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunTests.test_asyncio_run_set_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_custom_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_double_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_explicit_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_interrupt_cancelled_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_no_repr_is_call_on_the_task_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_non_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_run @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_run_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_run_keeps_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_run_non_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_second_with_block_raises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_set_event_loop_called_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_runners.RunnerTests.test_signal_install_not_supported_ok @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_accept_connection_multiple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_reader_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_reader_existing_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_writer_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_close_no_selector @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_make_socket_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_make_ssl_transport_without_ssl_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_process_events_read @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_process_events_read_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_process_events_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_process_events_write_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_read_from_self_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_read_from_self_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_reader_read_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_reader_unknown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_writer_read_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_writer_unknown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_sock_connect_resolve_using_socket_params @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_write_to_self_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_write_to_self_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_fatal_error_connected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_fatal_error_connected_custom_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_read_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_read_ready_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_read_ready_oserr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_read_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_buffer_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_buffer_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_connected_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_error_received @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_error_received_connected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_error_received @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_error_received_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_transport_inheritance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_buffer_updated_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_get_buffer_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_get_buffer_zerosized @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_proto_type_switch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_eof_received_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_conn_reset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_eof_keep_open @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_tryagain_interrupted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_ctor_with_waiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_data_received_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_pause_reading_connection_made @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_pause_resume_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_eof_received_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_conn_reset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_eof_keep_open @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_tryagain_interrupted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_transport_close_remove_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_eof_after_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_eof_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_partial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_partial_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_partial_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_partial_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_partial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_partial_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_sendmsg_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_writelines_send_full @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_writelines_send_partial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test__add_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test_abort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test_close_write_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test_connection_lost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test_fatal_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test_fatal_error_custom_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_selector_events.SelectorTransportTests.test_force_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_events.TestPyGetEventLoop.test_get_event_loop_returns_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TestPyGetEventLoop.test_get_event_loop_returns_running_loop2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TestServer.test_get_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TimerTests.test_hash @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TimerTests.test_timer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TimerTests.test_timer_comparison @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TimerTests.test_timer_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TimerTests.test_timer_repr_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_events.TimerTests.test_when @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.DuckTests.test_ensure_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.DuckTests.test_wrap_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_invoked_on_set_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_invoked_on_set_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_remove_first_and_second_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_remove_first_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_callbacks_remove_third_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_evil_call_soon_list_mutation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_remove_done_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_remove_done_callbacks_list_clear @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_remove_done_callbacks_list_mutation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_schedule_callbacks_list_mutation_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_schedule_callbacks_list_mutation_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_use_after_free_on_fut_callback_0_with_evil__eq__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_use_after_free_on_fut_callback_0_with_evil__getattribute__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureDoneCallbackTests.test_use_after_free_on_fut_context_0_with_evil__getattribute__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureInheritanceTests.test_inherit_without_calling_super_init @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_constructor_positional @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_constructor_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_constructor_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_constructor_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_copy_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_exception_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_cancel_message_getter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_cancel_message_setter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_cancelled_exception_refcycles @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_cancelled_result_refcycles @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_del_collect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_iter_throw @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_source_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_future_stop_iteration_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_generic_alias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_initial_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_isfuture @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_iter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_log_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_set_result_unless_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_stop_iteration_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_stop_iteration_subclass_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_abandoned @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_exception_result_retrieved @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_exception_retrieved @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_not_called_after_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_result_retrieved @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_tb_logger_result_unretrieved @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_uninitialized @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_wrap_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_cancel2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_wrap_future_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures.PyFutureTests.test_yield_from_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures2.FutureReprTests.test_recursive_repr_for_pending_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures2.PyFutureTests.test_future_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_futures2.PyFutureTests.test_handle_exc_handler_correct_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_abort_barrier @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_abort_barrier_when_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_abort_barrier_when_exception_then_resetting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_abort_barrier_when_tasks_half_draining_half_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_barrier @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_barrier_parties @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_blocking_tasks_while_draining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_draining_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_filling_one_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_filling_one_task_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_filling_task_by_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_filling_tasks_cancel_one @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_filling_tasks_check_return_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_filling_tasks_wait_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_reset_barrier @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_when_tasks_half_draining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_when_tasks_half_draining_half_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_while_tasks_draining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_while_tasks_waiting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.BarrierTests.test_reset_barrier_while_tasks_waiting_and_waiting_again @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_ambiguous_loops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_explicit_lock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_notify @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_notify_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_notify_all_unacquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_notify_unacquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_timeout_in_block @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_wait_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_wait_cancel_after_notify @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_wait_cancel_contested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_wait_for_unacquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.ConditionTests.test_wait_unacquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.EventTests.test_clear @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.EventTests.test_clear_with_waiters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.EventTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.EventTests.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.EventTests.test_wait_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.EventTests.test_wait_on_set @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_acquire @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_acquire_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_cancel_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_cancel_release_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_finished_waiter_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_lock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_lock_by_with_statement @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_lock_doesnt_accept_loop_parameter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_release_no_waiters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_release_not_acquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.LockTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_acquire @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_acquire_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_acquire_cancel_before_awoken @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_acquire_fifo_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_acquire_fifo_order_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_acquire_fifo_order_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_acquire_hang @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_acquire_no_hang @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_initial_value_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_release_no_waiters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_release_not_acquired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_locks.SemaphoreTests.test_semaphore_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.CoroutineTests.test_async_def_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.CoroutineTests.test_double_await @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.CoroutineTests.test_iscoroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.CoroutineTests.test_iscoroutine_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.CoroutineTests.test_iscoroutinefunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.CoroutineTests.test_task_print_stack @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.CoroutineTests.test_types_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.LockTests.test_context_manager_async_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.LockTests.test_context_manager_with_await @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_pep492.StreamReaderTests.test_readline @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_close_self_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_create_server @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_create_server_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_reading_aborted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_reading_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_reading_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_writing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_datagram_loop_writing_aborted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_loop_self_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_loop_self_reading_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_loop_self_reading_fut @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_make_datagram_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_make_socket_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_process_events @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_stop_serving @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.BaseProactorEventLoopTests.test_write_to_self @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test__loop_writing_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test__loop_writing_error_received @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test__loop_writing_error_received_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test__loop_writing_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_fatal_error_connected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_buffer_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_buffer_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_connected_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_error_received @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_error_received_connected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorDatagramTransportTests.test_sendto_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorEventLoopUnixSockSendfileTests.test_blocking_socket @ win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_abort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_call_connection_lost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close_invalid_sockobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close_protocol_connection_lost_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_close_write_fut @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_dont_pause_writing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_fatal_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_fatal_error_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_force_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_force_close_idempotent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_force_close_protocol_connection_lost_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_aborted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_aborted_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_aborted_is_fatal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_conn_reset_lost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_reading_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing_force_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_loop_writing_stop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_reading_connection_made @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_resume_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_resume_writing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_writing_2write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_pause_writing_3write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof_buffer_write_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof_duplex_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_eof_write_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_more @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_proactor_events.ProactorSocketTransportTests.test_write_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_protocols.ProtocolsAbsTests.test_base_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_protocols.ProtocolsAbsTests.test_buffered_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_protocols.ProtocolsAbsTests.test_datagram_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_protocols.ProtocolsAbsTests.test_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_protocols.ProtocolsAbsTests.test_subprocess_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.LifoQueueJoinTests.test_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.LifoQueueJoinTests.test_join_empty_queue @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.LifoQueueJoinTests.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.LifoQueueJoinTests.test_task_done_underflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.LifoQueueTests.test_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.PriorityQueueJoinTests.test_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.PriorityQueueJoinTests.test_join_empty_queue @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.PriorityQueueJoinTests.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.PriorityQueueJoinTests.test_task_done_underflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.PriorityQueueTests.test_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueBasicTests.test_empty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueBasicTests.test_full @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueBasicTests.test_generic_alias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueBasicTests.test_maxsize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueBasicTests.test_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueBasicTests.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueBasicTests.test_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_blocking_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_blocking_get_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_cancelled_getters_not_being_held_in_self_getters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_get_cancelled_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_get_with_putters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_get_with_waiting_putters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_nonblocking_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_nonblocking_get_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueGetTests.test_why_are_getters_waiting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueJoinTests.test_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueJoinTests.test_join_empty_queue @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueJoinTests.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueueJoinTests.test_task_done_underflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_blocking_put @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_blocking_put_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_cancelled_put_silence_value_error_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_cancelled_puts_not_being_held_in_self_putters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_float_maxsize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_get_cancel_drop_many_pending_readers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_get_cancel_drop_one_pending_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_nonblocking_put @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_nonblocking_put_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_put_cancel_drop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_put_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_put_cancelled_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_put_with_waiting_getters @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_queues.QueuePutTests.test_why_are_putters_waiting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_cancels_hanging_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_closes_gens_after_hanging_tasks_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_from_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_loop_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_only_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_raises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_reports_hanging_tasks_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_return @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunTests.test_asyncio_run_set_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_custom_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_double_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_explicit_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_interrupt_cancelled_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_no_repr_is_call_on_the_task_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_non_debug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_run @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_run_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_run_keeps_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_run_non_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_second_with_block_raises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_set_event_loop_called_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_runners.RunnerTests.test_signal_install_not_supported_ok @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_accept_connection_multiple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_reader_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_reader_existing_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_add_writer_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_close_no_selector @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_make_socket_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_make_ssl_transport_without_ssl_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_process_events_read @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_process_events_read_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_process_events_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_process_events_write_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_read_from_self_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_read_from_self_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_reader_read_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_reader_unknown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_writer_read_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_remove_writer_unknown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_sock_connect_resolve_using_socket_params @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_write_to_self_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.BaseSelectorEventLoopTests.test_write_to_self_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_fatal_error_connected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_fatal_error_connected_custom_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_read_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_read_ready_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_read_ready_oserr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_read_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_buffer_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_buffer_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_connected_addr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_error_received @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_error_received_connected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_error_received @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_error_received_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_sendto_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorDatagramTransportTests.test_transport_inheritance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_buffer_updated_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_get_buffer_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_get_buffer_zerosized @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_proto_type_switch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_eof_received_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_conn_reset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_eof_keep_open @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportBufferedProtocolTests.test_read_ready_tryagain_interrupted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_ctor_with_waiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_data_received_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_pause_reading_connection_made @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_pause_resume_reading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_eof_received_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_conn_reset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_eof_keep_open @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_read_ready_tryagain_interrupted @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_transport_close_remove_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_eof_after_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_eof_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_partial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_partial_bytearray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_partial_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_partial_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_partial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_partial_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_ready_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_sendmsg_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_write_tryagain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_writelines_send_full @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorSocketTransportTests.test_writelines_send_partial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test__add_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test_abort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test_close_write_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test_connection_lost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test_ctor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test_fatal_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test_fatal_error_custom_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_selector_events.SelectorTransportTests.test_force_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sendfile.ProactorEventLoopTests.test_sendfile_no_fallback_for_fallback_transport @ win32-AMD64 test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_close_peer_after_receiving @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_close_peer_in_the_middle_of_receiving @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -630,7 +648,7 @@ test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_fallback_clos test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_for_closing_transp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_force_fallback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_force_unsupported_native @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_no_fallback_for_fallback_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_no_fallback_for_fallback_transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_not_supported @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_partial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sendfile_pre_and_post_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -644,26 +662,27 @@ test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sock_sendfile_with_off test.test_asyncio.test_sendfile.SelectEventLoopTests.test_sock_sendfile_zero_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_server.SelectorStartServerTests.test_start_server_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_server.SelectorStartServerTests.test_start_unix_server_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_server.TestServer2.test_wait_closed_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_server.TestServer2.test_wait_closed_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_server.TestServer2.test_wait_closed_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_server.TestServer2.test_wait_closed_race @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_cancel_sock_accept @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_create_connection_sock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_huge_content @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_huge_content_recvinto @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_recvfrom @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_recvfrom_into @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_sendto_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_sock_accept @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_recvfrom @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_recvfrom_into @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_sendto_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_sock_accept @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_sock_client_connect_racing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_sock_client_fail @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_sock_client_ops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_sock_client_racing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_sock_lowlevel.SelectEventLoopTests.test_unix_sock_client_ops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_ssl.TestSSL.test_connect_accepted_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_ssl.TestSSL.test_connect_accepted_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_ssl.TestSSL.test_connect_timeout_warning @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_ssl.TestSSL.test_create_connection_ssl_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_ssl.TestSSL.test_create_connection_ssl_failed_certificate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_ssl.TestSSL.test_create_connection_ssl_slow_handshake @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_ssl.TestSSL.test_create_connection_ssl_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64 +!test.test_asyncio.test_ssl.TestSSL.test_create_connection_ssl_1 @ linux-x86_64-github +test.test_asyncio.test_ssl.TestSSL.test_create_connection_ssl_failed_certificate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_ssl.TestSSL.test_create_connection_ssl_slow_handshake @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_ssl.TestSSL.test_create_server_ssl_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_ssl.TestSSL.test_create_server_ssl_over_ssl @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_ssl.TestSSL.test_flush_before_shutdown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -678,56 +697,57 @@ test.test_asyncio.test_ssl.TestSSL.test_start_tls_client_buf_proto_1 @ darwin-ar test.test_asyncio.test_ssl.TestSSL.test_start_tls_client_reg_proto_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_ssl.TestSSL.test_start_tls_server_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_ssl.TestSSL.test_start_tls_slow_client_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_buf_feed_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_create_connection_memory_leak @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_create_connection_ssl_failed_certificate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_create_connection_ssl_slow_handshake @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_handshake_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_client_buf_proto_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_client_reg_proto_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_server_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_slow_client_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_wrong_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_close_during_handshake @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_close_during_ssl_over_ssl @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_connection_lost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_connection_lost_when_busy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_data_received_after_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_eof_received_waiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_fatal_error_no_name_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_get_extra_info_on_closed_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_handshake_timeout_negative @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_handshake_timeout_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_set_new_app_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_write_after_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_staggered.StaggeredTests.test_empty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_staggered.StaggeredTests.test_first_error_second_successful @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_staggered.StaggeredTests.test_first_timeout_second_successful @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_staggered.StaggeredTests.test_multiple_winners @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_staggered.StaggeredTests.test_none_successful @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_staggered.StaggeredTests.test_one_successful @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_IncompleteReadError_pickleable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_LimitOverrunError_pickleable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test___repr__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test___repr__data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test___repr__eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test___repr__exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test___repr__nondefault_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test___repr__transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test___repr__waiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_sslproto.ProactorStartTLSTests.test_buf_feed_data @ win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_buf_feed_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_create_connection_memory_leak @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_create_connection_ssl_failed_certificate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_create_connection_ssl_slow_handshake @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_handshake_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_client_buf_proto_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_client_reg_proto_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_server_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_slow_client_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SelectorStartTLSTests.test_start_tls_wrong_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_close_during_handshake @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_close_during_ssl_over_ssl @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_connection_lost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_connection_lost_when_busy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_data_received_after_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_eof_received_waiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_fatal_error_no_name_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_get_extra_info_on_closed_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_handshake_timeout_negative @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_handshake_timeout_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_set_new_app_protocol @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_sslproto.SslProtoHandshakeTests.test_write_after_closing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_staggered.StaggeredTests.test_empty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_staggered.StaggeredTests.test_first_error_second_successful @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_staggered.StaggeredTests.test_first_timeout_second_successful @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_staggered.StaggeredTests.test_multiple_winners @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_staggered.StaggeredTests.test_none_successful @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_staggered.StaggeredTests.test_one_successful @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_IncompleteReadError_pickleable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_LimitOverrunError_pickleable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test___repr__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test___repr__data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test___repr__eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test___repr__exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test___repr__nondefault_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test___repr__transport @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test___repr__waiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_streams.StreamTests.test_async_writer_api @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_streams.StreamTests.test_async_writer_api_exception_after_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_at_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_drain_raises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_streams.StreamTests.test_at_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_drain_raises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_streams.StreamTests.test_eof_feed_when_closing_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_exception_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_exception_waiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_feed_empty_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_feed_nonempty_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_invalid_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_streams.StreamTests.test_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_exception_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_exception_waiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_feed_empty_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_feed_nonempty_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_invalid_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_streams.StreamTests.test_loop_is_closed_resource_warnings @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_multiple_drain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_streams.StreamTests.test_multiple_drain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_streams.StreamTests.test_open_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_streams.StreamTests.test_open_connection_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_streams.StreamTests.test_open_connection_happy_eyeball_refcycles @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -735,40 +755,40 @@ test.test_asyncio.test_streams.StreamTests.test_open_connection_no_loop_ssl @ da test.test_asyncio.test_streams.StreamTests.test_open_unix_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_streams.StreamTests.test_open_unix_connection_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_streams.StreamTests.test_open_unix_connection_no_loop_ssl @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_read @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_read_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_read_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_read_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_read_line_breaks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_read_until_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_read_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readexactly @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readexactly_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readexactly_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readexactly_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readexactly_zero_or_less @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readline @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readline_empty_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readline_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readline_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readline_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readline_limit_with_existing_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readline_nolimit_nowait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readline_read_byte_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readuntil_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readuntil_limit_found_sep @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readuntil_multi_chunks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readuntil_multi_chunks_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_readuntil_separator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_start_server @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_start_tls @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_streams.StreamTests.test_read @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_read_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_read_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_read_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_read_line_breaks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_read_until_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_read_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readexactly @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readexactly_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readexactly_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readexactly_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readexactly_zero_or_less @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readline @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readline_empty_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readline_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readline_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readline_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readline_limit_with_existing_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readline_nolimit_nowait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readline_read_byte_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readuntil_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readuntil_limit_found_sep @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readuntil_multi_chunks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readuntil_multi_chunks_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_readuntil_separator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_start_server @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_start_tls @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_streams.StreamTests.test_start_unix_server @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_streamreader_constructor_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_streamreader_constructor_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_streamreader_constructor_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_streamreaderprotocol_constructor_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_streamreaderprotocol_constructor_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_streams.StreamTests.test_streamreaderprotocol_constructor_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_streams.StreamTests.test_streamreader_constructor_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_streamreader_constructor_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_streamreader_constructor_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_streamreaderprotocol_constructor_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_streamreaderprotocol_constructor_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_streams.StreamTests.test_streamreaderprotocol_constructor_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_streams.StreamTests.test_unhandled_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_streams.StreamTests.test_unhandled_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_streams.StreamTests.test_wait_closed_on_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -781,6 +801,10 @@ test.test_asyncio.test_subprocess.SubprocessFastWatcherTests.test_create_subproc test.test_asyncio.test_subprocess.SubprocessFastWatcherTests.test_popen_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_subprocess.SubprocessFastWatcherTests.test_popen_error_with_stdin_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_subprocess.SubprocessFastWatcherTests.test_read_stdout_after_process_exit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_subprocess.SubprocessProactorTests.test_create_subprocess_exec_text_mode_fails @ win32-AMD64 +test.test_asyncio.test_subprocess.SubprocessProactorTests.test_create_subprocess_shell_text_mode_fails @ win32-AMD64 +test.test_asyncio.test_subprocess.SubprocessProactorTests.test_popen_error @ win32-AMD64 +test.test_asyncio.test_subprocess.SubprocessProactorTests.test_popen_error_with_stdin_pipe @ win32-AMD64 test.test_asyncio.test_subprocess.SubprocessSafeWatcherTests.test_close_dont_kill_finished @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_subprocess.SubprocessSafeWatcherTests.test_create_subprocess_exec_text_mode_fails @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_subprocess.SubprocessSafeWatcherTests.test_create_subprocess_shell_text_mode_fails @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -823,361 +847,361 @@ test.test_asyncio.test_subprocess.SubprocessThreadedWatcherTests.test_subprocess test.test_asyncio.test_subprocess.SubprocessThreadedWatcherTests.test_subprocess_concurrent_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_subprocess.SubprocessThreadedWatcherTests.test_subprocess_protocol_events @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_subprocess.SubprocessThreadedWatcherTests.test_terminate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_subprocess.SubprocessTransportTests.test_proc_exited @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_subprocess.SubprocessTransportTests.test_subprocess_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_cancel_children_on_child_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_base_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_direct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_parent_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_propagate_cancellation_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_04 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_07 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_08 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_09 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_10 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_11 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_12 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_13 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_14 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_15 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_16 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_17 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_18 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_19 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_20 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_20a @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_21a @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_22 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_23 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_24 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_25 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_already_entered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_context_manager_exit_raises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_double_enter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_finished @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_no_create_task_after_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_not_entered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_task_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_task_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_without_parent_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__enter_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__enter_task_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__leave_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__leave_task_failure1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__leave_task_failure2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__register_task_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__register_task_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__register_task_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__unregister_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CIntrospectionTests.test__unregister_task_not_registered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_cancellation_broadcast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_constructor_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_constructor_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_constructor_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_exception_marking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_issue46672 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_one_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_result_exception_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_return_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.CoroutineGatherTests.test_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_empty_sequence_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_empty_sequence_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_empty_sequence_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_heterogenous_futures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_homogenous_futures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_one_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_one_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_result_exception_one_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_result_exception_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_return_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.FutureGatherTests.test_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.GenericTaskTests.test_future_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyCurrentLoopTests.test_current_task_no_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyCurrentLoopTests.test_current_task_no_running_loop_implicit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyCurrentLoopTests.test_current_task_with_implicit_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__enter_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__enter_task_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__leave_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__leave_task_failure1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__leave_task_failure2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__register_task_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__register_task_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__register_task_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__unregister_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyIntrospectionTests.test__unregister_task_not_registered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_concurrent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_coroutine_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_coroutine_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_reverse_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_with_unused_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_bare_create_named_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_bare_create_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_baseexception_during_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_at_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_awaited_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_both_task_and_inner_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_gather_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_gather_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_inner_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_task_catching @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_task_ignoring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_traceback_for_future_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_traceback_for_future_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_with_message_before_starting_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_with_message_then_future_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_with_message_then_future_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_yield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancellation_exception_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancelling @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_5 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_coroutine_non_gen_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_coroutine_non_gen_function_return_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_create_task_with_async_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_create_task_with_asynclike_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_create_task_with_noncoroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_current_task_with_interleaving_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_error_msg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_neither @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_task_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_exception_chaining_after_await @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_exception_chaining_after_await_with_context_cycle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_exception_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_gather_shield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_generic_alias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_get_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_get_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_iscoroutinefunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_log_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_other_loop_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_proper_refcounts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_cancel_inner @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_cancel_outer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_coroutine_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_coroutine_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_coroutine_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_effect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_shortcut @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_sleep @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_sleep_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_step_result_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_stop_while_run_in_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_subclasses_ctask_cfuture @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_awaits_on_itself @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_basics @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_cancel_message_getter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_cancel_message_setter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_cancel_sleeping_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_cancel_waiter_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_del_collect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_repr_autogenerated @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_repr_name_not_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_repr_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_set_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_set_name_pylong @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_tb_logger_not_called_after_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_uncancel_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_uncancel_structured_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_concurrent_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_first_completed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_first_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_first_exception_in_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_really_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_with_iterator_of_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_yield_future_passes_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_yield_wait_does_not_shield_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_concurrent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_coroutine_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_coroutine_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_reverse_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_with_unused_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_bare_create_named_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_bare_create_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_baseexception_during_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_at_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_awaited_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_both_task_and_inner_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_gather_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_gather_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_inner_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_task_catching @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_task_ignoring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_traceback_for_future_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_traceback_for_future_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_with_message_before_starting_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_with_message_then_future_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_with_message_then_future_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_yield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancellation_exception_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancelling @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_5 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_coroutine_non_gen_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_coroutine_non_gen_function_return_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_create_task_with_async_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_create_task_with_asynclike_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_create_task_with_noncoroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_current_task_with_interleaving_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_error_msg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_neither @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_task_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_exception_chaining_after_await @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_exception_chaining_after_await_with_context_cycle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_exception_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_gather_shield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_generic_alias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_get_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_get_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_iscoroutinefunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_log_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_other_loop_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_proper_refcounts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_set_exception_causes_invalid_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_set_result_causes_invalid_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_cancel_inner @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_cancel_outer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_coroutine_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_coroutine_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_coroutine_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_effect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_shortcut @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_sleep @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_sleep_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_step_result_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_stop_while_run_in_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_awaits_on_itself @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_basics @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_cancel_message_getter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_cancel_message_setter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_cancel_sleeping_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_cancel_waiter_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_del_collect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_repr_autogenerated @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_repr_name_not_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_repr_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_set_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_set_name_pylong @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_source_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_tb_logger_not_called_after_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_uncancel_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_uncancel_structured_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_concurrent_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_first_completed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_first_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_first_exception_in_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_really_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_with_iterator_of_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_yield_future_passes_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_yield_wait_does_not_shield_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe_task_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe_task_factory_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_tasks.SleepTests.test_sleep_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_threads.ToThreadTests.test_to_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_threads.ToThreadTests.test_to_thread_args_kwargs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_threads.ToThreadTests.test_to_thread_concurrent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_threads.ToThreadTests.test_to_thread_contextvars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_threads.ToThreadTests.test_to_thread_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_threads.ToThreadTests.test_to_thread_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_cancel_in_timeout_after_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_foreign_cancel_doesnt_timeout_if_not_expired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_foreign_exception_on_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_foreign_exception_passed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_nested_timeout_in_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_nested_timeouts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_nested_timeouts_concurrent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_nested_timeouts_loop_busy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_outer_task_is_not_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_repr_active @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_repr_disabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_repr_expired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_repr_finished @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_reschedule @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_after_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_already_entered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_at_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_at_disabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_disabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_double_enter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_exception_cause @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_expired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_expiring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_finished @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_in_the_past_sleep_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_not_called @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_not_entered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_without_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_zero_sleep_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_timeouts.TimeoutTests.test_waiter_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_transports.TransportTests.test_ctor_extra_is_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_transports.TransportTests.test_dgram_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_transports.TransportTests.test_flowcontrol_mixin_set_write_limits @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_transports.TransportTests.test_get_extra_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_transports.TransportTests.test_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_transports.TransportTests.test_subprocess_transport_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_transports.TransportTests.test_writelines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_subprocess.SubprocessTransportTests.test_proc_exited @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_subprocess.SubprocessTransportTests.test_subprocess_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_cancel_children_on_child_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_base_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_direct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_parent_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_exception_refcycles_propagate_cancellation_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_04 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_07 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_08 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_09 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_10 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_11 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_12 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_13 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_14 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_15 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_16 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_17 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_18 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_19 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_20 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_20a @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_21a @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_22 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_23 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_24 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_25 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_already_entered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_context_manager_exit_raises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_double_enter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_finished @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_no_create_task_after_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_not_entered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_task_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_task_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_taskgroups.TestTaskGroup.test_taskgroup_without_parent_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__enter_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__enter_task_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__leave_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__leave_task_failure1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__leave_task_failure2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__register_task_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__register_task_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__register_task_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__unregister_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CIntrospectionTests.test__unregister_task_not_registered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_cancellation_broadcast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_constructor_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_constructor_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_constructor_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_exception_marking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_issue46672 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_one_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_result_exception_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_return_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.CoroutineGatherTests.test_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_empty_sequence_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_empty_sequence_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_empty_sequence_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_heterogenous_futures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_constructor_homogenous_futures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_one_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_one_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_result_exception_one_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_result_exception_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_return_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.FutureGatherTests.test_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.GenericTaskTests.test_future_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyCurrentLoopTests.test_current_task_no_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyCurrentLoopTests.test_current_task_no_running_loop_implicit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyCurrentLoopTests.test_current_task_with_implicit_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__enter_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__enter_task_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__leave_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__leave_task_failure1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__leave_task_failure2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__register_task_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__register_task_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__register_task_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__unregister_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyIntrospectionTests.test__unregister_task_not_registered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_concurrent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_coroutine_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_coroutine_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_reverse_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_as_completed_with_unused_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_bare_create_named_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_bare_create_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_baseexception_during_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_at_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_awaited_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_both_task_and_inner_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_gather_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_gather_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_inner_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_task_catching @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_task_ignoring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_traceback_for_future_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_traceback_for_future_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_with_message_before_starting_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_with_message_then_future_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_with_message_then_future_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancel_yield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancellation_exception_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_cancelling @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_5 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_context_6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_coroutine_non_gen_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_coroutine_non_gen_function_return_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_create_task_with_async_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_create_task_with_asynclike_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_create_task_with_noncoroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_current_task_with_interleaving_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_error_msg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_neither @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_ensure_future_task_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_exception_chaining_after_await @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_exception_chaining_after_await_with_context_cycle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_exception_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_gather_shield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_generic_alias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_get_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_get_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_iscoroutinefunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_log_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_other_loop_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_proper_refcounts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_cancel_inner @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_cancel_outer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_coroutine_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_coroutine_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_coroutine_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_effect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_shield_shortcut @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_sleep @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_sleep_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_step_result_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_stop_while_run_in_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_subclasses_ctask_cfuture @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_awaits_on_itself @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_basics @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_cancel_message_getter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_cancel_message_setter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_cancel_sleeping_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_cancel_waiter_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_del_collect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_repr_autogenerated @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_repr_name_not_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_repr_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_set_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_task_set_name_pylong @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_tb_logger_not_called_after_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_uncancel_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_uncancel_structured_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_concurrent_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_first_completed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_first_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_first_exception_in_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_really_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_with_iterator_of_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_wait_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_yield_future_passes_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_SubclassTests.test_yield_wait_does_not_shield_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_concurrent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_coroutine_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_coroutine_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_reverse_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_as_completed_with_unused_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_bare_create_named_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_bare_create_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_baseexception_during_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_at_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_awaited_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_both_task_and_inner_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_gather_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_gather_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_inner_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_task_catching @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_task_ignoring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_traceback_for_future_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_traceback_for_future_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_with_message_before_starting_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_with_message_then_future_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_with_message_then_future_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancel_yield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancellation_exception_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_cancelling @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_5 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_context_6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_coroutine_non_gen_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_coroutine_non_gen_function_return_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_create_task_with_async_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_create_task_with_asynclike_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_create_task_with_noncoroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_current_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_current_task_with_interleaving_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_error_msg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_neither @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_ensure_future_task_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_exception_chaining_after_await @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_exception_chaining_after_await_with_context_cycle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_exception_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_gather_shield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_generic_alias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_get_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_get_coro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_iscoroutinefunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_log_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_other_loop_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_proper_refcounts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_set_exception_causes_invalid_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_set_result_causes_invalid_state @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_cancel_inner @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_cancel_outer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_coroutine_use_global_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_coroutine_use_running_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_coroutine_without_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_effect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_gather @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_shield_shortcut @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_sleep @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_sleep_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_step_result_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_stop_while_run_in_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_awaits_on_itself @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_basics @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_cancel_message_getter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_cancel_message_setter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_cancel_sleeping_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_cancel_waiter_future @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_del_collect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_repr_autogenerated @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_repr_name_not_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_repr_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_set_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_set_name_pylong @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_task_source_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_tb_logger_not_called_after_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_uncancel_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_uncancel_structured_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_concurrent_complete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_duplicate_coroutines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_first_completed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_first_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_first_exception_in_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_really_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_with_iterator_of_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_wait_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_yield_future_passes_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.PyTask_PyFuture_Tests.test_yield_wait_does_not_shield_cancel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe_task_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe_task_factory_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.RunCoroutineThreadsafeTests.test_run_coroutine_threadsafe_with_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_tasks.SleepTests.test_sleep_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_threads.ToThreadTests.test_to_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_threads.ToThreadTests.test_to_thread_args_kwargs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_threads.ToThreadTests.test_to_thread_concurrent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_threads.ToThreadTests.test_to_thread_contextvars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_threads.ToThreadTests.test_to_thread_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_threads.ToThreadTests.test_to_thread_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_cancel_in_timeout_after_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_foreign_cancel_doesnt_timeout_if_not_expired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_foreign_exception_on_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_foreign_exception_passed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_nested_timeout_in_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_nested_timeouts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_nested_timeouts_concurrent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_nested_timeouts_loop_busy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_outer_task_is_not_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_repr_active @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_repr_disabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_repr_expired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_repr_finished @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_reschedule @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_after_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_already_entered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_at_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_at_disabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_disabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_double_enter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_exception_cause @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_expired @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_expiring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_finished @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_in_the_past_sleep_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_not_called @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_not_entered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_without_task @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_timeout_zero_sleep_zero @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_timeouts.TimeoutTests.test_waiter_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_transports.TransportTests.test_ctor_extra_is_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_transports.TransportTests.test_dgram_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_transports.TransportTests.test_flowcontrol_mixin_set_write_limits @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_transports.TransportTests.test_get_extra_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_transports.TransportTests.test_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_transports.TransportTests.test_subprocess_transport_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_transports.TransportTests.test_writelines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_asyncio.test_unix_events.AbstractChildWatcherTests.test_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_unix_events.AbstractChildWatcherTests.test_warns_on_subclassing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_unix_events.BaseChildWatcherTests.test_not_implemented @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1288,22 +1312,28 @@ test.test_asyncio.test_unix_events.UnixWritePipeTransportTests.test_write_eof_pe test.test_asyncio.test_unix_events.UnixWritePipeTransportTests.test_write_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_unix_events.UnixWritePipeTransportTests.test_write_no_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_asyncio.test_unix_events.UnixWritePipeTransportTests.test_write_partial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_asyncio_wait_for_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_asyncio_wait_for_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_cancel_blocking_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_cancel_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_cancel_suppressed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_cancellation_race_condition @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_issue86296 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_race_condition @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_reraises_exception_during_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_timeout_less_then_0_or_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_timeout_less_then_0_or_0_coroutine_do_not_started @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_timeout_less_then_0_or_0_future_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_waits_for_task_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_waits_for_task_cancellation_w_timeout_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.WaitForShieldTests.test_none_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.WaitForShieldTests.test_shielded_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_asyncio.test_waitfor.WaitForShieldTests.test_zero_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_asyncio_wait_for_cancelled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_asyncio_wait_for_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_cancel_blocking_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_cancel_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_blocking @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_cancel_suppressed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_cancellation_race_condition @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_issue86296 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_race_condition @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_reraises_exception_during_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_timeout_less_then_0_or_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_timeout_less_then_0_or_0_coroutine_do_not_started @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_timeout_less_then_0_or_0_future_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_waits_for_task_cancellation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.AsyncioWaitForTest.test_wait_for_waits_for_task_cancellation_w_timeout_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.WaitForShieldTests.test_none_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.WaitForShieldTests.test_shielded_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_waitfor.WaitForShieldTests.test_zero_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_asyncio.test_windows_events.ProactorMultithreading.test_run_from_nonmain_thread @ win32-AMD64 +test.test_asyncio.test_windows_events.ProactorTests.test_connect_pipe_cancel @ win32-AMD64 +test.test_asyncio.test_windows_events.ProactorTests.test_double_bind @ win32-AMD64 +test.test_asyncio.test_windows_events.ProactorTests.test_loop_restart @ win32-AMD64 +test.test_asyncio.test_windows_events.WinPolicyTests.test_proactor_win_policy @ win32-AMD64 +test.test_asyncio.test_windows_events.WinPolicyTests.test_selector_win_policy @ win32-AMD64 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_audit.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_audit.txt new file mode 100644 index 0000000000..bcbf4516ab --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_audit.txt @@ -0,0 +1,6 @@ +test.test_audit.AuditTest.test_basic @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_audit.AuditTest.test_block_add_hook @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_audit.AuditTest.test_block_add_hook_baseexception @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_audit.AuditTest.test_mmap @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_audit.AuditTest.test_socket @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_audit.AuditTest.test_sqlite3 @ darwin-arm64,linux-aarch64,linux-x86_64 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_bdb.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_bdb.txt index aa951b4fec..9d6fbeff3a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_bdb.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_bdb.txt @@ -4,15 +4,13 @@ test.test_bdb.BreakpointTestCase.test_bp_exception_on_condition_evaluation @ dar test.test_bdb.BreakpointTestCase.test_bp_ignore_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.BreakpointTestCase.test_bp_on_non_existent_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.BreakpointTestCase.test_clear_at_no_bp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_bdb.BreakpointTestCase.test_clear_two_bp_on_same_line @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_bdb.BreakpointTestCase.test_clear_two_bp_on_same_line @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.BreakpointTestCase.test_disabled_temporary_bp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.BreakpointTestCase.test_ignore_count_on_disabled_bp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.BreakpointTestCase.test_load_bps_from_previous_Bdb_instance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.BreakpointTestCase.test_temporary_bp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.IssuesTestCase.test_next_to_botframe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_bdb.IssuesTestCase.test_next_until_return_in_generator @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -# TODO: GR-71863 -!test.test_bdb.IssuesTestCase.test_next_until_return_in_generator @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_bdb.IssuesTestCase.test_next_until_return_in_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.IssuesTestCase.test_step_at_return_with_no_trace_in_caller @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.RunTestCase.test_run_step @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_bdb.RunTestCase.test_runeval_step @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_buffer.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_buffer.txt index 4f5915c083..933abf5f5f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_buffer.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_buffer.txt @@ -1,6 +1,7 @@ test.test_buffer.TestBufferProtocol.test_issue_7385 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_memoryview_array @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_buffer.TestBufferProtocol.test_memoryview_cast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_buffer.TestBufferProtocol.test_memoryview_cast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_buffer.TestBufferProtocol.test_memoryview_cast_1D_ND @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_memoryview_cast_zero_shape @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_memoryview_cast_zero_strides @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_memoryview_check_released @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -12,25 +13,28 @@ test.test_buffer.TestBufferProtocol.test_memoryview_redirect @ darwin-arm64,linu test.test_buffer.TestBufferProtocol.test_memoryview_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_memoryview_sequence @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_memoryview_serializing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_buffer.TestBufferProtocol.test_memoryview_tolist @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_buffer.TestBufferProtocol.test_memoryview_tolist @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_cmp_contig @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_fortran @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_hash @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github # Transient lists differ error GR-49936 !test.test_buffer.TestBufferProtocol.test_ndarray_index_getitem_multidim +test.test_buffer.TestBufferProtocol.test_ndarray_index_getitem_single @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_index_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_index_null_strides @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_index_scalar @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_buffer.TestBufferProtocol.test_ndarray_index_setitem_single @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_linked_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_memoryview_from_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_random_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_re_export @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_buffer.TestBufferProtocol.test_ndarray_sequence @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github # Transient lists differ error GR-49936 !test.test_buffer.TestBufferProtocol.test_ndarray_slice_assign_multidim !test.test_buffer.TestBufferProtocol.test_ndarray_slice_multidim !test.test_buffer.TestBufferProtocol.test_ndarray_slice_redundant_suboffsets -test.test_buffer.TestBufferProtocol.test_ndarray_slice_zero_shape @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_buffer.TestBufferProtocol.test_ndarray_slice_zero_shape @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_tolist_null_strides @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_zero_shape @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_buffer.TestBufferProtocol.test_ndarray_zero_strides @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_capi.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_capi.txt index 13df6c50e0..b84521977b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_capi.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_capi.txt @@ -1,8 +1,10 @@ -test.test_capi.test_abstract.CAPITest.test_mapping_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64-github +test.test_capi.test_abstract.CAPITest.test_mapping_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_mapping_haskey @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_mapping_haskeystring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_mapping_keys_valuesitems @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_abstract.CAPITest.test_mapping_keys_valuesitems_bad_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_mapping_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_abstract.CAPITest.test_object_ascii @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_object_bytes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_object_delattr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_object_getattr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -13,6 +15,7 @@ test.test_capi.test_abstract.CAPITest.test_object_setattr @ darwin-arm64,linux-a test.test_capi.test_abstract.CAPITest.test_object_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_sequence_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_abstract.CAPITest.test_sequence_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_abstract.CAPITest.test_sequence_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_bytearray.CAPITest.test_asstring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_bytearray.CAPITest.test_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_bytearray.CAPITest.test_checkexact @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -58,6 +61,7 @@ test.test_capi.test_dict.CAPITest.test_dict_getitemwitherror @ darwin-arm64,linu test.test_capi.test_dict.CAPITest.test_dict_keys_valuesitems_bad_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_dict.CAPITest.test_dict_new @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_dict.CAPITest.test_dict_next @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_dict.CAPITest.test_dict_setdefault @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_dict.CAPITest.test_dict_setitem @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_dict.CAPITest.test_dict_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_dict.CAPITest.test_dictproxy_new @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -68,9 +72,10 @@ test.test_capi.test_eval_code_ex.PyEval_EvalCodeExTests.test_with_args @ darwin- test.test_capi.test_eval_code_ex.PyEval_EvalCodeExTests.test_with_closure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_eval_code_ex.PyEval_EvalCodeExTests.test_with_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_eval_code_ex.PyEval_EvalCodeExTests.test_with_kwarg_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_eval_code_ex.PyEval_EvalCodeExTests.test_with_kwargs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_exceptions.Test_ErrSetAndRestore.test_err_restore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_exceptions.Test_ErrSetAndRestore.test_err_set_raised @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_capi.test_exceptions.Test_ErrSetAndRestore.test_format @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_exceptions.Test_ErrSetAndRestore.test_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_float.CAPIFloatTest.test_asdouble @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_float.CAPIFloatTest.test_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_float.CAPIFloatTest.test_checkexact @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -167,6 +172,7 @@ test.test_capi.test_list.CAPITest.test_list_setslice @ darwin-arm64,linux-aarch6 test.test_capi.test_list.CAPITest.test_list_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_list.CAPITest.test_list_sort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_long.LongTests.test_compact @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_long.LongTests.test_compact_known @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_long.LongTests.test_long_aslongandoverflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_long.LongTests.test_long_aslonglong @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_long.LongTests.test_long_aslonglongandoverflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -187,6 +193,7 @@ test.test_capi.test_misc.CAPITest.test_buildvalue @ darwin-arm64,linux-aarch64,l test.test_capi.test_misc.CAPITest.test_buildvalue_ints @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.CAPITest.test_c_type_with_ipow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.CAPITest.test_c_type_with_matrix_multiplication @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_misc.CAPITest.test_docstring_signature_parsing @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.CAPITest.test_export_symbols @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.CAPITest.test_getitem_with_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_capi.test_misc.CAPITest.test_heaptype_with_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -219,6 +226,7 @@ test.test_capi.test_misc.CAPITest.test_unstable_gc_new_with_extra_data @ darwin- test.test_capi.test_misc.TestStaticTypes.test_pytype_ready_always_sets_tp_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_ModuleStateAccess.test_get_module_bad_def @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_ModuleStateAccess.test_get_module_static_in_mro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_misc.Test_ModuleStateAccess.test_state_access @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_ModuleStateAccess.test_subclass_get_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_ModuleStateAccess.test_subclass_get_module_with_super @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_testcapi.test_PyOS_mystricmp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -231,7 +239,7 @@ test.test_capi.test_misc.Test_testcapi.test_dict_iteration @ darwin-arm64,linux- test.test_capi.test_misc.Test_testcapi.test_empty_argparse @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_testcapi.test_from_contiguous @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_testcapi.test_from_spec_metatype_inheritance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_capi.test_misc.Test_testcapi.test_frozenset_add_in_capi @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_capi.test_misc.Test_testcapi.test_frozenset_add_in_capi @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_testcapi.test_gc_control @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_testcapi.test_gc_visit_objects_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_misc.Test_testcapi.test_gc_visit_objects_exit_early @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -274,7 +282,9 @@ test.test_capi.test_misc.Test_testcapi.test_xincref_doesnt_leak @ darwin-arm64,l test.test_capi.test_number.CAPITest.test_asssizet @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_number.CAPITest.test_binary_ops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_number.CAPITest.test_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_number.CAPITest.test_float @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_number.CAPITest.test_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_number.CAPITest.test_long @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_number.CAPITest.test_misc_add @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_number.CAPITest.test_misc_power @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_number.CAPITest.test_unary_ops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -298,15 +308,18 @@ test.test_capi.test_structmembers.ReadWriteTests_NewAPI.test_inplace_string @ da test.test_capi.test_structmembers.ReadWriteTests_OldAPI.test_bad_assignments @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_structmembers.ReadWriteTests_OldAPI.test_bool @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_structmembers.ReadWriteTests_OldAPI.test_inplace_string @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_capi.test_sys.CAPITest.test_sys_formatstderr @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_capi.test_sys.CAPITest.test_sys_formatstdout @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_capi.test_sys.CAPITest.test_sys_writestderr @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_capi.test_sys.CAPITest.test_sys_writestdout @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_sys.CAPITest.test_sys_formatstderr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_sys.CAPITest.test_sys_formatstdout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_sys.CAPITest.test_sys_writestderr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_sys.CAPITest.test_sys_writestdout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_tuple.CAPITest.test_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_tuple.CAPITest.test_tuple_checkexact @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_tuple.CAPITest.test_tuple_get_item @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_tuple.CAPITest.test_tuple_get_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_tuple.CAPITest.test_tuple_getitem @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_tuple.CAPITest.test_tuple_getslice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_tuple.CAPITest.test_tuple_new @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_tuple.CAPITest.test_tuple_pack @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_tuple.CAPITest.test_tuple_set_item @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_capi.test_tuple.CAPITest.test_tuple_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_capi.test_unicode.CAPITest.test_from_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_charmapcodec.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_charmapcodec.txt index 1eb2782d92..9153d17231 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_charmapcodec.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_charmapcodec.txt @@ -1,4 +1,4 @@ -test.test_charmapcodec.CharmapCodecTest.test_constructorx @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_charmapcodec.CharmapCodecTest.test_constructory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_charmapcodec.CharmapCodecTest.test_encodex @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_charmapcodec.CharmapCodecTest.test_maptoundefined @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_charmapcodec.CharmapCodecTest.test_constructorx @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_charmapcodec.CharmapCodecTest.test_constructory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_charmapcodec.CharmapCodecTest.test_encodex @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_charmapcodec.CharmapCodecTest.test_maptoundefined @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cmd_line.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cmd_line.txt index e93de3b44f..c7c2b78e47 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cmd_line.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cmd_line.txt @@ -5,6 +5,7 @@ test.test_cmd_line.CmdLineTest.test_coding @ darwin-arm64,linux-aarch64,linux-aa test.test_cmd_line.CmdLineTest.test_del___main__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_cmd_line.CmdLineTest.test_directories @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.CmdLineTest.test_empty_PYTHONPATH_issue16309 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_cmd_line.CmdLineTest.test_invalid_utf8_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_cmd_line.CmdLineTest.test_isolatedmode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.CmdLineTest.test_large_PYTHONPATH @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.CmdLineTest.test_non_ascii @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -16,8 +17,11 @@ test.test_cmd_line.CmdLineTest.test_run_module @ darwin-arm64,linux-aarch64,linu test.test_cmd_line.CmdLineTest.test_run_module_bug1764407 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.CmdLineTest.test_site_flag @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.CmdLineTest.test_stdin_readline @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_cmd_line.CmdLineTest.test_stdout_flush_at_shutdown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_cmd_line.CmdLineTest.test_unbuffered_input @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.CmdLineTest.test_unbuffered_output @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.CmdLineTest.test_unmached_quote @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.CmdLineTest.test_verbose @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cmd_line.IgnoreEnvironmentTest.test_ignore_PYTHONPATH @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_cmd_line.SyntaxErrorTests.test_decoding_error_at_the_end_of_the_line @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_cmd_line.SyntaxErrorTests.test_tokenizer_error_with_stdin @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecencodings_jp.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecencodings_jp.txt index 978ba2468f..a63026ec70 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecencodings_jp.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecencodings_jp.txt @@ -38,6 +38,20 @@ test.test_codecencodings_jp.Test_SJISX0213.test_incrementaldecoder @ darwin-arm6 test.test_codecencodings_jp.Test_SJISX0213.test_incrementalencoder_error_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecencodings_jp.Test_SJISX0213.test_streamwriter_reset_no_pending @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecencodings_jp.Test_SJISX0213.test_xmlcharrefreplace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_callback_None_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_callback_backward_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_callback_forward_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_callback_index_outofbound @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_callback_returns_bytes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_callback_wrong_objects @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_chunkcoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_customreplace_encode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_incrementaldecoder @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_incrementalencoder @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_incrementalencoder_error_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_streamwriter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_streamwriter_reset_no_pending @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecencodings_jp.Test_SJIS_2004.test_xmlcharrefreplace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecencodings_jp.Test_SJIS_COMPAT.test_callback_None_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecencodings_jp.Test_SJIS_COMPAT.test_callback_backward_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecencodings_jp.Test_SJIS_COMPAT.test_callback_forward_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_cn.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_cn.txt index 7e0dd2f6dd..ff73c64b96 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_cn.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_cn.txt @@ -1,7 +1,7 @@ -test.test_codecmaps_cn.TestGB18030Map.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_cn.TestGB18030Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_cn.TestGB2312Map.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_cn.TestGB2312Map.test_mapping_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_cn.TestGB2312Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_cn.TestGBKMap.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_cn.TestGBKMap.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecmaps_cn.TestGB18030Map.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_cn.TestGB18030Map.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_cn.TestGB2312Map.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_cn.TestGB2312Map.test_mapping_file @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_cn.TestGB2312Map.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_cn.TestGBKMap.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_cn.TestGBKMap.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_hk.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_hk.txt index 23769ffb10..57eabca95c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_hk.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_hk.txt @@ -1,3 +1,3 @@ -test.test_codecmaps_hk.TestBig5HKSCSMap.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_hk.TestBig5HKSCSMap.test_mapping_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_hk.TestBig5HKSCSMap.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecmaps_hk.TestBig5HKSCSMap.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_hk.TestBig5HKSCSMap.test_mapping_file @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_hk.TestBig5HKSCSMap.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_jp.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_jp.txt index 0c1c9184b5..c4b676cb39 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_jp.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_jp.txt @@ -1,9 +1,9 @@ -test.test_codecmaps_jp.TestCP932Map.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_jp.TestEUCJISX0213Map.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_jp.TestEUCJISX0213Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_jp.TestEUCJPCOMPATMap.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_jp.TestEUCJPCOMPATMap.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_jp.TestSJISCOMPATMap.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_jp.TestSJISCOMPATMap.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_jp.TestSJISX0213Map.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_jp.TestSJISX0213Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecmaps_jp.TestCP932Map.test_errorhandle @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_jp.TestEUCJISX0213Map.test_errorhandle @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_jp.TestEUCJISX0213Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_jp.TestEUCJPCOMPATMap.test_errorhandle @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_jp.TestEUCJPCOMPATMap.test_mapping_supplemental @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_jp.TestSJISCOMPATMap.test_errorhandle @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_jp.TestSJISCOMPATMap.test_mapping_supplemental @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_jp.TestSJISX0213Map.test_errorhandle @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_jp.TestSJISX0213Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_kr.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_kr.txt index 4c8aad78f8..16babf315d 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_kr.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_kr.txt @@ -1,9 +1,9 @@ -test.test_codecmaps_kr.TestCP949Map.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_kr.TestCP949Map.test_mapping_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_kr.TestCP949Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_kr.TestEUCKRMap.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_kr.TestEUCKRMap.test_mapping_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_kr.TestEUCKRMap.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_kr.TestJOHABMap.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_kr.TestJOHABMap.test_mapping_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_kr.TestJOHABMap.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecmaps_kr.TestCP949Map.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_kr.TestCP949Map.test_mapping_file @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_kr.TestCP949Map.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_kr.TestEUCKRMap.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_kr.TestEUCKRMap.test_mapping_file @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_kr.TestEUCKRMap.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_kr.TestJOHABMap.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_kr.TestJOHABMap.test_mapping_file @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_kr.TestJOHABMap.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_tw.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_tw.txt index b7403fdea3..8c31d2e7c8 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_tw.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecmaps_tw.txt @@ -1,5 +1,5 @@ -test.test_codecmaps_tw.TestBIG5Map.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_tw.TestBIG5Map.test_mapping_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_tw.TestBIG5Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_tw.TestCP950Map.test_errorhandle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_codecmaps_tw.TestCP950Map.test_mapping_supplemental @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecmaps_tw.TestBIG5Map.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_tw.TestBIG5Map.test_mapping_file @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_tw.TestBIG5Map.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_tw.TestCP950Map.test_errorhandle @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_codecmaps_tw.TestCP950Map.test_mapping_supplemental @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecs.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecs.txt index a5c109d354..96b968ab7a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecs.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_codecs.txt @@ -7,6 +7,7 @@ test.test_codecs.BasicUnicodeTest.test_encoding_map_type_initialized @ darwin-ar test.test_codecs.CharmapTest.test_decode_with_int2int_map @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecs.CharmapTest.test_decode_with_int2str_map @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecs.CharmapTest.test_decode_with_string_map @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_codecs.CodePageTest.test_mbcs_alias @ win32-AMD64 test.test_codecs.CodecNameNormalizationTest.test_codecs_lookup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecs.CodecNameNormalizationTest.test_encodings_normalize_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_codecs.CodecsModuleTest.test_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_compile.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_compile.txt index b3e8006518..142516cba6 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_compile.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_compile.txt @@ -66,10 +66,8 @@ test.test_compile.TestSpecifics.test_unary_minus @ darwin-arm64,linux-aarch64,li !test.test_compile.TestStackSizeStability.test_for_break_continue_inside_try_finally_block @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 !test.test_compile.TestStackSizeStability.test_for_break_continue_inside_with_block @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 !test.test_compile.TestStackSizeStability.test_for_else @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -!test.test_compile.TestStackSizeStability.test_if @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_compile.TestStackSizeStability.test_if @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -test.test_compile.TestStackSizeStability.test_if_else @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_compile.TestStackSizeStability.test_if_else @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_compile.TestStackSizeStability.test_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_compile.TestStackSizeStability.test_if_else @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github !test.test_compile.TestStackSizeStability.test_return_inside_async_with_block @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 !test.test_compile.TestStackSizeStability.test_return_inside_except_block @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 !test.test_compile.TestStackSizeStability.test_return_inside_finally_block @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 @@ -82,6 +80,5 @@ test.test_compile.TestStackSizeStability.test_if_else @ linux-aarch64-github,lin !test.test_compile.TestStackSizeStability.test_try_except_star_finally @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 !test.test_compile.TestStackSizeStability.test_try_except_star_qualified @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 !test.test_compile.TestStackSizeStability.test_try_finally @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -!test.test_compile.TestStackSizeStability.test_while_else @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_compile.TestStackSizeStability.test_while_else @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_compile.TestStackSizeStability.test_while_else @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github !test.test_compile.TestStackSizeStability.test_with @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_compileall.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_compileall.txt index 9e1fdc5cfc..3880fa7e59 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_compileall.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_compileall.txt @@ -25,8 +25,7 @@ test.test_compileall.CommandLineTestsNoSourceEpoch.test_recursion_limit @ darwin test.test_compileall.CommandLineTestsNoSourceEpoch.test_regexp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CommandLineTestsNoSourceEpoch.test_silent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CommandLineTestsNoSourceEpoch.test_symlink_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -# Transient failure -!test.test_compileall.CommandLineTestsNoSourceEpoch.test_workers +test.test_compileall.CommandLineTestsNoSourceEpoch.test_workers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CommandLineTestsNoSourceEpoch.test_workers_available_cores @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CommandLineTestsWithSourceEpoch.test_compiles_as_much_as_possible @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CommandLineTestsWithSourceEpoch.test_d_compile_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -55,8 +54,7 @@ test.test_compileall.CommandLineTestsWithSourceEpoch.test_recursion_limit @ darw test.test_compileall.CommandLineTestsWithSourceEpoch.test_regexp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CommandLineTestsWithSourceEpoch.test_silent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CommandLineTestsWithSourceEpoch.test_symlink_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -# Transient failure -!test.test_compileall.CommandLineTestsWithSourceEpoch.test_workers +test.test_compileall.CommandLineTestsWithSourceEpoch.test_workers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CommandLineTestsWithSourceEpoch.test_workers_available_cores @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CompileallTestsWithSourceEpoch.test_compile_dir_maxlevels @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_compileall.CompileallTestsWithSourceEpoch.test_compile_dir_pathlike @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_concurrent_futures.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_concurrent_futures.txt index 41a67a60a2..b189f0fc1b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_concurrent_futures.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_concurrent_futures.txt @@ -7,7 +7,11 @@ test.test_concurrent_futures.test_as_completed.ThreadPoolAsCompletedTest.test_co test.test_concurrent_futures.test_as_completed.ThreadPoolAsCompletedTest.test_duplicate_futures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_as_completed.ThreadPoolAsCompletedTest.test_future_times_out @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_as_completed.ThreadPoolAsCompletedTest.test_no_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_crash_at_task_unpickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +# Transiently times out GR-65714 +!test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_crash_at_task_unpickle +test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_crash_big_data @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_crash_during_func_exec_on_worker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_crash_during_result_pickle_on_worker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_error_at_task_pickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_error_at_task_unpickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_error_during_func_exec_on_worker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -17,8 +21,8 @@ test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest. test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_exit_during_func_exec_on_worker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_exit_during_result_pickle_on_worker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_exit_during_result_unpickle_in_result_handler @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -# Transiently times out GR-65714 !test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_gh105829_should_not_deadlock_if_wakeup_pipe_full +test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_shutdown_deadlock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github !test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_shutdown_deadlock_pickle !test.test_concurrent_futures.test_future.FutureTests.test_cancel !test.test_concurrent_futures.test_future.FutureTests.test_cancelled diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib.txt index c360fe184c..565bf67268 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib.txt @@ -51,11 +51,13 @@ test.test_contextlib.TestExitStack.test_dont_reraise_RuntimeError @ darwin-arm64 test.test_contextlib.TestExitStack.test_enter_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.TestExitStack.test_enter_context_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.TestExitStack.test_excessive_nesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_contextlib.TestExitStack.test_exit_exception_chaining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.TestExitStack.test_exit_exception_chaining_reference @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.TestExitStack.test_exit_exception_chaining_suppress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_contextlib.TestExitStack.test_exit_exception_explicit_none_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.TestExitStack.test_exit_exception_non_suppressing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_contextlib.TestExitStack.test_exit_exception_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib.TestExitStack.test_exit_exception_with_correct_context @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_contextlib.TestExitStack.test_exit_exception_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_contextlib.TestExitStack.test_exit_exception_with_correct_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.TestExitStack.test_exit_exception_with_existing_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.TestExitStack.test_exit_raise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib.TestExitStack.test_exit_suppress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib_async.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib_async.txt index 05acf7a10f..c2b850a963 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib_async.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_contextlib_async.txt @@ -1,52 +1,56 @@ -test.test_contextlib_async.AclosingTestCase.test_aclosing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AclosingTestCase.test_aclosing_bpo41229 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AclosingTestCase.test_aclosing_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_contextlib_async.AclosingTestCase.test_aclosing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AclosingTestCase.test_aclosing_bpo41229 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AclosingTestCase.test_aclosing_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_contextlib_async.AclosingTestCase.test_instance_docs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_attribs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_doc_attrib @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_except @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_except_stopiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_no_reraise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_non_normalised @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_plain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_trap_no_yield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_trap_second_yield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_trap_yield_after_throw @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_wrap_runtimeerror @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_decorating_method @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_decorator_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_instance_docstring_given_cm_docstring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_keywords @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.AsyncContextManagerTestCase.test_recursive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAbstractAsyncContextManager.test_async_gen_propagates_generator_exit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAbstractAsyncContextManager.test_enter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_except @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_except_stopiter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_no_reraise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_non_normalised @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_plain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_trap_no_yield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_trap_second_yield @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_trap_yield_after_throw @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_contextmanager_wrap_runtimeerror @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_decorating_method @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_decorator_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_instance_docstring_given_cm_docstring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_keywords @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.AsyncContextManagerTestCase.test_recursive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAbstractAsyncContextManager.test_async_gen_propagates_generator_exit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAbstractAsyncContextManager.test_enter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_contextlib_async.TestAbstractAsyncContextManager.test_exit_is_abstract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_contextlib_async.TestAbstractAsyncContextManager.test_structural_subclassing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_contextlib_async.TestAsyncExitStack.test_async_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_async_push @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_body_exception_suppress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_dont_reraise_RuntimeError @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_enter_async_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_enter_async_context_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_enter_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_enter_context_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_excessive_nesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_chaining_reference @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_chaining_suppress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_non_suppressing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_with_correct_context @ linux-aarch64-github,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_with_existing_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_exit_raise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_exit_suppress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_instance_bypass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_instance_bypass_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_instance_docs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_no_resources @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_pop_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncExitStack.test_push @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_contextlib_async.TestAsyncNullcontext.test_async_nullcontext @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_contextlib_async.TestAsyncExitStack.test_async_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_async_exit_exception_chaining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_async_exit_exception_explicit_none_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_async_push @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_body_exception_suppress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_dont_reraise_RuntimeError @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_enter_async_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_enter_async_context_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_enter_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_enter_context_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_excessive_nesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_chaining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_chaining_reference @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_chaining_suppress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_explicit_none_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_non_suppressing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_with_correct_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_exception_with_existing_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_raise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_exit_suppress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_instance_bypass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_instance_bypass_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_instance_docs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_no_resources @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_pop_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncExitStack.test_push @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_contextlib_async.TestAsyncNullcontext.test_async_nullcontext @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_coroutines.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_coroutines.txt index f1802c18eb..1c7758f221 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_coroutines.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_coroutines.txt @@ -1,7 +1,7 @@ test.test_coroutines.AsyncBadSyntaxTest.test_badsyntax_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.AsyncBadSyntaxTest.test_badsyntax_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.AsyncBadSyntaxTest.test_badsyntax_4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_coroutines.CoroAsyncIOCompatTest.test_asyncio_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_coroutines.CoroAsyncIOCompatTest.test_asyncio_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_coroutines.CoroutineTest.test_await_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.CoroutineTest.test_await_10 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.CoroutineTest.test_await_11 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -36,6 +36,7 @@ test.test_coroutines.CoroutineTest.test_cr_await @ darwin-arm64,linux-aarch64,li test.test_coroutines.CoroutineTest.test_cr_frame_after_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.CoroutineTest.test_for_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.CoroutineTest.test_for_11 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_coroutines.CoroutineTest.test_for_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.CoroutineTest.test_for_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.CoroutineTest.test_for_4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.CoroutineTest.test_for_6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -72,4 +73,4 @@ test.test_coroutines.CoroutineTest.test_with_4 @ darwin-arm64,linux-aarch64,linu test.test_coroutines.CoroutineTest.test_with_5 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.CoroutineTest.test_with_9 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_coroutines.TokenizerRegrTest.test_oneline_defs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_coroutines.UnawaitedWarningDuringShutdownTest.test_unawaited_warning_during_shutdown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_coroutines.UnawaitedWarningDuringShutdownTest.test_unawaited_warning_during_shutdown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cppext.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cppext.txt index 58f58f9257..ab09b8a686 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cppext.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cppext.txt @@ -1,2 +1,2 @@ -test.test_cppext.TestCPPExt.test_build_cpp03 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_cppext.TestCPPExt.test_build_cpp03 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_cppext.TestCPPExt.test_build_cpp11 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cprofile.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cprofile.txt index 0b840528a2..652421a1c2 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cprofile.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_cprofile.txt @@ -1,13 +1,10 @@ test.test_cprofile.CProfileTest.test_output_file_when_changing_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# transient: inconsistent running time [GR-67706] -!test.test_cprofile.CProfileTest.test_run_profile_as_module +test.test_cprofile.CProfileTest.test_run_profile_as_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cprofile.CProfileTest.test_throw @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_cprofile.TestCommandLine.test_sort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71916 -!test.test_profile.ProfileTest.test_calling_conventions @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -!test.test_profile.ProfileTest.test_cprofile @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_profile.ProfileTest.test_calling_conventions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_profile.ProfileTest.test_cprofile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_profile.ProfileTest.test_output_file_when_changing_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_profile.ProfileTest.test_run @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# This test times out in the gate even though it succeeds locally and in the retagger. Race condition? -!test.test_profile.ProfileTest.test_run_profile_as_module +test.test_profile.ProfileTest.test_run_profile_as_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_profile.ProfileTest.test_runctx @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ctypes.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ctypes.txt index 437131fdf6..9dea4d7af5 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ctypes.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ctypes.txt @@ -20,42 +20,42 @@ test.test_ctypes.test_arrays.ArrayTestCase.test_step_overflow @ darwin-arm64,lin test.test_ctypes.test_arrays.ArrayTestCase.test_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_arrays.ArrayTestCase.test_zero_length @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_byval @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_callbacks_2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_longlong_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_callbacks_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_longlong_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_pointers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_recursive_as_param @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_shorts @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_shorts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_struct_return_2H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_struct_return_8H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamNestedWrapperTestCase.test_wchar_parm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_byval @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_callbacks_2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_longlong_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_callbacks_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_longlong_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_pointers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_recursive_as_param @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_shorts @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_shorts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_struct_return_2H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_struct_return_8H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamPropertyWrapperTestCase.test_wchar_parm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_byval @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_callbacks_2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_longlong_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_callbacks_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_longlong_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_pointers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_recursive_as_param @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_shorts @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_shorts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_struct_return_2H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_struct_return_8H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.AsParamWrapperTestCase.test_wchar_parm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_byval @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_callbacks_2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_longlong_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_callbacks_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_longlong_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_pointers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_recursive_as_param @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_shorts @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_shorts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_struct_return_2H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_struct_return_8H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_as_parameter.BasicWrapTestCase.test_wchar_parm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -109,35 +109,37 @@ test.test_ctypes.test_c_simple_type_meta.PyCSimpleTypeAsMetaclassTest.test_creat test.test_ctypes.test_c_simple_type_meta.PyCSimpleTypeAsMetaclassTest.test_creating_pointer_in_dunder_init_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_c_simple_type_meta.PyCSimpleTypeAsMetaclassTest.test_creating_pointer_in_dunder_new_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_c_simple_type_meta.PyCSimpleTypeAsMetaclassTest.test_creating_pointer_in_dunder_new_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_byte @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_char @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_double @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_float @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_int @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_issue12483 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_long @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_longdouble @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_longlong @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_short @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_ubyte @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_uint @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_ulong @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_ulonglong @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_unsupported_restype_1 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_unsupported_restype_2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.Callbacks.test_ushort @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_callback_large_struct @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_callback_register_double @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_callback_register_int @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_callback_too_many_args @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_convert_result_error @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_integrate @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_issue_8959_a @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_byte @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_char @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_double @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_float @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_i38748_stackCorruption @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_int @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_issue12483 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_long @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_longdouble @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_longlong @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_short @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_ubyte @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_uint @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_ulong @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_ulonglong @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_unsupported_restype_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_unsupported_restype_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.Callbacks.test_ushort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_callback_large_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_callback_register_double @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_callback_register_int @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_callback_too_many_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_convert_result_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_integrate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_issue_8959_a @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_callbacks.SampleCallbacksTestCase.test_issue_8959_b @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_callbacks.StdcallCallbacks.test_byte @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_callbacks.StdcallCallbacks.test_char @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_callbacks.StdcallCallbacks.test_double @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_callbacks.StdcallCallbacks.test_float @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_callbacks.StdcallCallbacks.test_i38748_stackCorruption @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_callbacks.StdcallCallbacks.test_int @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_callbacks.StdcallCallbacks.test_issue12483 @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_callbacks.StdcallCallbacks.test_long @ win32-AMD64,win32-AMD64-github @@ -186,6 +188,34 @@ test.test_ctypes.test_cfuncs.CFunctions.test_ulonglong_plus @ darwin-arm64,linux test.test_ctypes.test_cfuncs.CFunctions.test_ushort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_cfuncs.CFunctions.test_ushort_plus @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_cfuncs.CFunctions.test_void @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_byte @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_byte_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_callwithresult @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_double @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_double_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_float @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_float_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_int @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_int_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_long @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_long_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_longdouble @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_longdouble_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_longlong @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_longlong_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_short @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_short_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_ubyte @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_ubyte_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_uint @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_uint_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_ulong @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_ulong_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_ulonglong @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_ulonglong_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_ushort @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_ushort_plus @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_cfuncs.stdcallCFunctions.test_void @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_checkretval.Test.test_checkretval @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_checkretval.Test.test_oledll @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_delattr.TestCase.test_chararray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -207,28 +237,30 @@ test.test_ctypes.test_frombuffer.Test.test_from_buffer_copy_with_offset @ darwin test.test_ctypes.test_frombuffer.Test.test_from_buffer_memoryview @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_frombuffer.Test.test_from_buffer_with_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_funcptr.CFuncPtrTestCase.test_abstract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_funcptr.CFuncPtrTestCase.test_basic @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_funcptr.CFuncPtrTestCase.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_funcptr.CFuncPtrTestCase.test_dllfunctions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_funcptr.CFuncPtrTestCase.test_first @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_funcptr.CFuncPtrTestCase.test_structures @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_funcptr.CFuncPtrTestCase.test_first @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_funcptr.CFuncPtrTestCase.test_structures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_byval @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_functions.FunctionTestCase.test_c_char_parm @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_functions.FunctionTestCase.test_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_functions.FunctionTestCase.test_callbacks_2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_functions.FunctionTestCase.test_c_char_parm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_functions.FunctionTestCase.test_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_functions.FunctionTestCase.test_callbacks_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_doubleresult @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_floatresult @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_intresult @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_longdoubleresult @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_functions.FunctionTestCase.test_longlong_callbacks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_functions.FunctionTestCase.test_longlong_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_longlongresult @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_mro @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_pointers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_functions.FunctionTestCase.test_sf1651235 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_functions.FunctionTestCase.test_shorts @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_functions.FunctionTestCase.test_sf1651235 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_functions.FunctionTestCase.test_shorts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_stringresult @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_struct_return_2H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_functions.FunctionTestCase.test_struct_return_2H_stdcall @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_struct_return_8H @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_functions.FunctionTestCase.test_struct_return_8H_stdcall @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_voidresult @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_wchar_parm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_functions.FunctionTestCase.test_wchar_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -246,7 +278,7 @@ test.test_ctypes.test_keeprefs.SimpleTestCase.test_cint @ darwin-arm64,linux-aar test.test_ctypes.test_keeprefs.StructureTestCase.test_ccharp_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_keeprefs.StructureTestCase.test_cint_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_keeprefs.StructureTestCase.test_struct_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_libc.LibTest.test_qsort @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_libc.LibTest.test_qsort @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_libc.LibTest.test_sqrt @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_loading.LoaderTest.test_1703286_A @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_loading.LoaderTest.test_1703286_B @ win32-AMD64,win32-AMD64-github @@ -254,6 +286,7 @@ test.test_ctypes.test_loading.LoaderTest.test_find @ darwin-arm64,linux-aarch64, test.test_ctypes.test_loading.LoaderTest.test_load @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_loading.LoaderTest.test_load_hasattr @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_loading.LoaderTest.test_load_library @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_loading.LoaderTest.test_load_ordinal_functions @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_loading.LoaderTest.test_load_version @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ctypes.test_macholib.MachOTest.test_find @ darwin-arm64 test.test_ctypes.test_macholib.MachOTest.test_framework_info @ darwin-arm64 @@ -296,34 +329,34 @@ test.test_ctypes.test_pep3118.Test.test_endian_types @ darwin-arm64,linux-aarch6 test.test_ctypes.test_pep3118.Test.test_native_types @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_0.test_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_0.test_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_pickling.PickleTest_0.test_unpickable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_pickling.PickleTest_0.test_unpickable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_0.test_wchar @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_1.test_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_1.test_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_pickling.PickleTest_1.test_unpickable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_pickling.PickleTest_1.test_unpickable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_1.test_wchar @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_2.test_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_2.test_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_pickling.PickleTest_2.test_unpickable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_pickling.PickleTest_2.test_unpickable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_2.test_wchar @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_3.test_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_3.test_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_pickling.PickleTest_3.test_unpickable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_pickling.PickleTest_3.test_unpickable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_3.test_wchar @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_4.test_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_4.test_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_pickling.PickleTest_4.test_unpickable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_pickling.PickleTest_4.test_unpickable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_4.test_wchar @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_5.test_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_5.test_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_pickling.PickleTest_5.test_unpickable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_pickling.PickleTest_5.test_unpickable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pickling.PickleTest_5.test_wchar @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_abstract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_basics @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_bug_1467852 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_c_void_p @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_pointers.PointersTestCase.test_callbacks_with_pointers @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_pointers.PointersTestCase.test_callbacks_with_pointers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_change_pointers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_charpp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_from_address @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -333,7 +366,7 @@ test.test_ctypes.test_pointers.PointersTestCase.test_pointer_crash @ darwin-arm6 test.test_ctypes.test_pointers.PointersTestCase.test_pointer_type_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_pointer_type_str_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_pointers.PointersTestCase.test_pointers_bool @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_prototypes.ArrayTest.test @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_prototypes.ArrayTest.test @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_prototypes.CharPointersTestCase.test_POINTER_c_char_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_prototypes.CharPointersTestCase.test_c_char_p_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_prototypes.CharPointersTestCase.test_c_void_p_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -344,14 +377,14 @@ test.test_ctypes.test_prototypes.CharPointersTestCase.test_paramflags @ darwin-a test.test_ctypes.test_prototypes.WCharPointersTestCase.test_POINTER_c_wchar_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_prototypes.WCharPointersTestCase.test_c_wchar_p_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_python_api.PythonAPITestCase.test_PyBytes_FromStringAndSize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_python_api.PythonAPITestCase.test_PyOS_snprintf @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_python_api.PythonAPITestCase.test_PyOS_snprintf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_python_api.PythonAPITestCase.test_pyobject_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_random_things.CallbackTracbackTestCase.test_FloatDivisionError @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_random_things.CallbackTracbackTestCase.test_IntegerDivisionError @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_random_things.CallbackTracbackTestCase.test_TypeErrorDivisionError @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_random_things.CallbackTracbackTestCase.test_ValueError @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_random_things.CallbackTracbackTestCase.test_FloatDivisionError @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_random_things.CallbackTracbackTestCase.test_IntegerDivisionError @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_random_things.CallbackTracbackTestCase.test_TypeErrorDivisionError @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_random_things.CallbackTracbackTestCase.test_ValueError @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_random_things.call_function_TestCase.test @ win32-AMD64,win32-AMD64-github -test.test_ctypes.test_refcounts.AnotherLeak.test_callback @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_refcounts.AnotherLeak.test_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_repr.ReprTest.test_char @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_repr.ReprTest.test_numbers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_returnfuncptrs.ReturnFuncPtrTestCase.test_from_dll @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -359,8 +392,8 @@ test.test_ctypes.test_returnfuncptrs.ReturnFuncPtrTestCase.test_from_dll_refcoun test.test_ctypes.test_returnfuncptrs.ReturnFuncPtrTestCase.test_with_prototype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_returnfuncptrs.ReturnFuncPtrTestCase.test_without_prototype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_simplesubclasses.Test.test_compare @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_simplesubclasses.Test.test_ignore_retval @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ctypes.test_simplesubclasses.Test.test_int_callback @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_simplesubclasses.Test.test_ignore_retval @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ctypes.test_simplesubclasses.Test.test_int_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_simplesubclasses.Test.test_int_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_sizes.SizesTestCase.test_16 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_sizes.SizesTestCase.test_32 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -445,6 +478,7 @@ test.test_ctypes.test_varsize_struct.VarSizeTest.test_array_invalid_length @ dar test.test_ctypes.test_varsize_struct.VarSizeTest.test_resize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_varsize_struct.VarSizeTest.test_zerosized_array @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_win32.FunctionCallTestCase.test_noargs @ win32-AMD64,win32-AMD64-github +test.test_ctypes.test_win32.ReturnStructSizesTestCase.test_sizes @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_win32.Structures.test_struct_by_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ctypes.test_win32.TestWintypes.test_COMError @ win32-AMD64,win32-AMD64-github test.test_ctypes.test_win32.TestWintypes.test_HWND @ win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_datetime.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_datetime.txt index a576f56f86..0b2c693b13 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_datetime.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_datetime.txt @@ -1,941 +1,943 @@ test.datetimetester.IranTest_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.IranTest_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.IranTest_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.IranTest_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.IranTest_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.IranTest_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.IranTest_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github -test.datetimetester.Oddballs_Fast.test_bug_1028306 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.Oddballs_Fast.test_check_arg_types @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.Oddballs_Fast.test_extra_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.Oddballs_Pure.test_bug_1028306 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.Oddballs_Pure.test_check_arg_types @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.Oddballs_Pure.test_extra_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateOnly_Fast.test_delta_non_days_ignored @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateOnly_Pure.test_delta_non_days_ignored @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_argument_passing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_astimezone_default_near_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_astimezone_default_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_aware_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_aware_subtract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_bad_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_even_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_extreme_hashes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_more_astimezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +!test.datetimetester.IranTest_Pure.test_system_transitions @ darwin-arm64 +test.datetimetester.Oddballs_Fast.test_bug_1028306 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.Oddballs_Fast.test_check_arg_types @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.Oddballs_Fast.test_extra_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.Oddballs_Pure.test_bug_1028306 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.Oddballs_Pure.test_check_arg_types @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.Oddballs_Pure.test_extra_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateOnly_Fast.test_delta_non_days_ignored @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateOnly_Pure.test_delta_non_days_ignored @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_argument_passing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_astimezone_default_near_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_astimezone_default_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_aware_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_aware_subtract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_bad_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_even_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_extreme_hashes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_more_astimezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.datetimetester.TestDateTimeTZ_Fast.test_negative_float_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.TestDateTimeTZ_Fast.test_negative_float_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.TestDateTimeTZ_Fast.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_subclass_datetimetz @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_trivial @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tz_aware_arithmetic @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_utc_offset_out_of_bounds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_utctimetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Fast.test_zones @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_argument_passing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_astimezone_default_near_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_astimezone_default_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_aware_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_aware_subtract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_bad_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_even_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_extreme_hashes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_more_astimezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.datetimetester.TestDateTimeTZ_Fast.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_subclass_datetimetz @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_trivial @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tz_aware_arithmetic @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_tzinfo_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_utc_offset_out_of_bounds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_utctimetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Fast.test_zones @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_argument_passing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_astimezone_default_near_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_astimezone_default_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_aware_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_aware_subtract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_bad_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_even_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_extreme_hashes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_more_astimezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.datetimetester.TestDateTimeTZ_Pure.test_negative_float_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.TestDateTimeTZ_Pure.test_negative_float_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.TestDateTimeTZ_Pure.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_strftime_with_bad_tzname_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_subclass_datetimetz @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_trivial @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tz_aware_arithmetic @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_utc_offset_out_of_bounds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_utctimetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTimeTZ_Pure.test_zones @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.datetimetester.TestDateTimeTZ_Pure.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_strftime_with_bad_tzname_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_subclass_datetimetz @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_trivial @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tz_aware_arithmetic @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_tzinfo_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_utc_offset_out_of_bounds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_utctimetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTimeTZ_Pure.test_zones @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.datetimetester.TestDateTime_Fast.test_negative_float_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.TestDateTime_Fast.test_negative_float_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.TestDateTime_Fast.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Fast.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.datetimetester.TestDateTime_Fast.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Fast.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.datetimetester.TestDateTime_Pure.test_negative_float_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.TestDateTime_Pure.test_negative_float_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.TestDateTime_Pure.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_strftime_with_bad_tzname_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDateTime_Pure.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Fast.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestDate_Pure.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_fromtimestamp_low_fold_detection @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_hash @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_hash_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_member @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_mixed_compare_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_mixed_compare_gap @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_mixed_compare_regular @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_pickle_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_utcoffset @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_vilnius_1941_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Fast.test_vilnius_1941_toutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_fromtimestamp_low_fold_detection @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_hash @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_hash_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_member @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_mixed_compare_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_mixed_compare_gap @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_mixed_compare_regular @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_pickle_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_utcoffset @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_vilnius_1941_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestLocalTimeDisambiguation_Pure.test_vilnius_1941_toutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestModule_Fast.test_all @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestModule_Fast.test_constants @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestModule_Fast.test_utc_alias @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestModule_Pure.test_all @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestModule_Pure.test_constants @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestModule_Pure.test_divide_and_round @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestModule_Pure.test_utc_alias @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.datetimetester.TestDateTime_Pure.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_strftime_with_bad_tzname_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDateTime_Pure.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Fast.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestDate_Pure.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_fromtimestamp_low_fold_detection @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_hash @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_hash_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_member @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_mixed_compare_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_mixed_compare_gap @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_mixed_compare_regular @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_pickle_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_utcoffset @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_vilnius_1941_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Fast.test_vilnius_1941_toutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_fromtimestamp_low_fold_detection @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_hash @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_hash_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_member @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_mixed_compare_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_mixed_compare_gap @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_mixed_compare_regular @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_pickle_fold @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_utcoffset @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_vilnius_1941_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestLocalTimeDisambiguation_Pure.test_vilnius_1941_toutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestModule_Fast.test_all @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestModule_Fast.test_constants @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestModule_Fast.test_utc_alias @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestModule_Pure.test_all @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestModule_Pure.test_constants @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestModule_Pure.test_divide_and_round @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestModule_Pure.test_utc_alias @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.datetimetester.TestSubclassDateTime_Fast.test_negative_float_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.TestSubclassDateTime_Fast.test_negative_float_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.TestSubclassDateTime_Fast.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Fast.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.datetimetester.TestSubclassDateTime_Fast.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Fast.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_combine @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_extract @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_extreme_ordinals @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_extreme_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisocalendar_type_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisocalendar_value_errors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_ambiguous @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_date_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_datetime_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_fails_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_fails_surrogate @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_separators @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromisoformat_utc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromtimestamp_keyword_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_fromtimestamp_with_none_arg @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_insane_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_insane_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_iso_long_years @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_isocalendar @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_isocalendar_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_more_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_more_ctime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_more_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_more_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_more_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.datetimetester.TestSubclassDateTime_Pure.test_negative_float_fromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.TestSubclassDateTime_Pure.test_negative_float_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.TestSubclassDateTime_Pure.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_strftime_with_bad_tzname_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestSubclassDateTime_Pure.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Fast.test_issue23600 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Fast.test_non_abstractness @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Fast.test_normal @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Fast.test_pickling_base @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Fast.test_pickling_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Fast.test_refcnt_crash_bug_22044 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Fast.test_subclass_must_override @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Pure.test_issue23600 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Pure.test_non_abstractness @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Pure.test_normal @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Pure.test_pickling_base @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Pure.test_pickling_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Pure.test_refcnt_crash_bug_22044 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTZInfo_Pure.test_subclass_must_override @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_carries @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_computations @ linux-aarch64-github,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_constructor @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_disallowed_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_disallowed_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_division @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_divmod @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_issue31293 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_issue31752 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_massive_normalization @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_remainder @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_subclass_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Fast.test_total_seconds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_carries @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_constructor @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_disallowed_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_disallowed_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_division @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_divmod @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_issue31293 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_issue31752 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_massive_normalization @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_remainder @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_subclass_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeDelta_Pure.test_total_seconds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_1653736 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_argument_passing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_aware_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_bad_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_empty @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_fractions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_time_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_hash_edge_cases @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_more_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_pickling_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_subclass_timetz @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_utc_offset_out_of_bounds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Fast.test_zones @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_1653736 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_argument_passing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_aware_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_bad_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_empty @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_fractions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_time_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_hash_edge_cases @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_more_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_pickling_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_subclass_timetz @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_utc_offset_out_of_bounds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeTZ_Pure.test_zones @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_aware_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_class_members @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_comparison_with_tzinfo @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_constructor @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_copy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_deepcopy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_inheritance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_offset_boundaries @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_pickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_tzname @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Fast.test_utcoffset @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_aware_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_class_members @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_comparison_with_tzinfo @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_constructor @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_copy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_deepcopy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_inheritance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_offset_boundaries @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_pickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_tzname @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimeZone_Pure.test_utcoffset @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_1653736 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_pickling_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Fast.test_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_1653736 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_pickling_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTime_Pure.test_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimezoneConversions_Fast.test_bogus_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimezoneConversions_Fast.test_easy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimezoneConversions_Fast.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimezoneConversions_Fast.test_tricky @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimezoneConversions_Pure.test_bogus_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimezoneConversions_Pure.test_easy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimezoneConversions_Pure.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.datetimetester.TestTimezoneConversions_Pure.test_tricky @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.datetimetester.TestSubclassDateTime_Pure.test_ordinal_conversions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_pickling_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_pickling_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_strftime_trailing_percent @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_strftime_with_bad_tzname_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_strftime_y2k @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_strptime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_strptime_single_digit @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_subclass_alternate_constructors @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_subclass_alternate_constructors_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_subclass_now @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_timestamp_aware @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_timestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_timetuple @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_today @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_tz_independent_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_utcfromtimestamp @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_utcfromtimestamp_limits @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_utcnow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestSubclassDateTime_Pure.test_weekday @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Fast.test_issue23600 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Fast.test_non_abstractness @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Fast.test_normal @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Fast.test_pickling_base @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Fast.test_pickling_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Fast.test_refcnt_crash_bug_22044 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Fast.test_subclass_must_override @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Pure.test_issue23600 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Pure.test_non_abstractness @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Pure.test_normal @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Pure.test_pickling_base @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Pure.test_pickling_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Pure.test_refcnt_crash_bug_22044 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTZInfo_Pure.test_subclass_must_override @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_carries @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_computations @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_constructor @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_disallowed_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_disallowed_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_division @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_divmod @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_issue31293 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_issue31752 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_massive_normalization @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_remainder @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_subclass_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Fast.test_total_seconds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_carries @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_constructor @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_disallowed_computations @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_disallowed_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_division @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_divmod @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_issue31293 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_issue31752 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_massive_normalization @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_microsecond_rounding @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_overflow @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_remainder @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_subclass_date @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_subclass_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_subclass_timedelta @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeDelta_Pure.test_total_seconds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_1653736 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_argument_passing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_aware_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_bad_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_empty @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_fractions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_time_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_hash_edge_cases @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_more_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_pickling_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_subclass_timetz @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_utc_offset_out_of_bounds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Fast.test_zones @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_1653736 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_argument_passing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_aware_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_bad_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_empty @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_fromisoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_fails @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_fails_typeerror @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_fractions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_subclass @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_time_examples @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_timespecs @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_fromisoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_hash_edge_cases @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_mixed_compare @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_more_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_pickling_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_subclass_timetz @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_tzinfo_classes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_utc_offset_out_of_bounds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeTZ_Pure.test_zones @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_aware_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_class_members @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_comparison_with_tzinfo @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_constructor @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_copy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_deepcopy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_inheritance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_offset_boundaries @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_pickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_tzname @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Fast.test_utcoffset @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_aware_datetime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_class_members @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_comparison_with_tzinfo @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_constructor @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_copy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_deepcopy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_inheritance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_offset_boundaries @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_pickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_tzname @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimeZone_Pure.test_utcoffset @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_1653736 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_pickling_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Fast.test_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_1653736 @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_backdoor_resistance @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_bad_constructor_arguments @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_basic_attributes @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_basic_attributes_nonzero @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_bool @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_comparing @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_compat_unpickle @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_format @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_harmful_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_harmless_mixed_comparison @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_hash_equality @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_isoformat @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_isoformat_timezone @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_pickling @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_pickling_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_repr @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_resolution_info @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_roundtrip @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_str @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_strftime @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_strftime_special @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_subclass_replace @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTime_Pure.test_subclass_time @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimezoneConversions_Fast.test_bogus_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimezoneConversions_Fast.test_easy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimezoneConversions_Fast.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimezoneConversions_Fast.test_tricky @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimezoneConversions_Pure.test_bogus_dst @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimezoneConversions_Pure.test_easy @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimezoneConversions_Pure.test_fromutc @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.datetimetester.TestTimezoneConversions_Pure.test_tricky @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.datetimetester.ZoneInfoTest[Africa/Abidjan]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Abidjan]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Abidjan]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -944,16 +946,18 @@ test.datetimetester.ZoneInfoTest[Africa/Abidjan]_Pure.test_gaps @ darwin-arm64,d test.datetimetester.ZoneInfoTest[Africa/Abidjan]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Accra]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Accra]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Africa/Accra]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Africa/Accra]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Africa/Accra]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Accra]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Africa/Accra]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Africa/Accra]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Africa/Addis_Ababa]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Addis_Ababa]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Addis_Ababa]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Addis_Ababa]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Addis_Ababa]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Addis_Ababa]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Addis_Ababa]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Addis_Ababa]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Algiers]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Algiers]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Algiers]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -963,9 +967,11 @@ test.datetimetester.ZoneInfoTest[Africa/Algiers]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[Africa/Asmara]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Asmara]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Asmara]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Asmara]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Asmara]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Asmara]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Asmara]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Asmara]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Bamako]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Bamako]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Bamako]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64 @@ -1016,10 +1022,10 @@ test.datetimetester.ZoneInfoTest[Africa/Cairo]_Pure.test_gaps @ darwin-arm64,dar test.datetimetester.ZoneInfoTest[Africa/Cairo]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Casablanca]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Casablanca]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Africa/Casablanca]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Africa/Casablanca]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Casablanca]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Casablanca]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Africa/Casablanca]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Africa/Casablanca]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Ceuta]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Ceuta]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Ceuta]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1041,15 +1047,19 @@ test.datetimetester.ZoneInfoTest[Africa/Dakar]_Pure.test_system_transitions @ da test.datetimetester.ZoneInfoTest[Africa/Dar_es_Salaam]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Dar_es_Salaam]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Dar_es_Salaam]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Dar_es_Salaam]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Dar_es_Salaam]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Dar_es_Salaam]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Dar_es_Salaam]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Dar_es_Salaam]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Djibouti]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Djibouti]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Djibouti]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Africa/Djibouti]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Djibouti]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Djibouti]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Djibouti]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Africa/Djibouti]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Douala]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Douala]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Douala]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1058,10 +1068,10 @@ test.datetimetester.ZoneInfoTest[Africa/Douala]_Pure.test_gaps @ darwin-arm64,da test.datetimetester.ZoneInfoTest[Africa/Douala]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/El_Aaiun]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/El_Aaiun]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Africa/El_Aaiun]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Africa/El_Aaiun]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/El_Aaiun]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/El_Aaiun]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Africa/El_Aaiun]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Africa/El_Aaiun]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Freetown]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Freetown]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Freetown]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64 @@ -1095,9 +1105,11 @@ test.datetimetester.ZoneInfoTest[Africa/Juba]_Pure.test_system_transitions @ dar test.datetimetester.ZoneInfoTest[Africa/Kampala]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Kampala]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Kampala]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Kampala]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Kampala]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Kampala]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Kampala]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Kampala]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Khartoum]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Khartoum]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Khartoum]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1179,9 +1191,11 @@ test.datetimetester.ZoneInfoTest[Africa/Mbabane]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[Africa/Mogadishu]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Mogadishu]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Mogadishu]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Mogadishu]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Mogadishu]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Mogadishu]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Mogadishu]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Africa/Mogadishu]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Monrovia]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Monrovia]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Monrovia]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1191,9 +1205,11 @@ test.datetimetester.ZoneInfoTest[Africa/Monrovia]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[Africa/Nairobi]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Nairobi]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Nairobi]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Africa/Nairobi]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Nairobi]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Nairobi]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Nairobi]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Africa/Nairobi]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Africa/Ndjamena]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Ndjamena]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Africa/Ndjamena]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1352,22 +1368,24 @@ test.datetimetester.ZoneInfoTest[America/Argentina/Ushuaia]_Pure.test_gaps @ dar test.datetimetester.ZoneInfoTest[America/Argentina/Ushuaia]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Aruba]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Aruba]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Aruba]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Aruba]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Aruba]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Aruba]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Aruba]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Aruba]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Asuncion]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Asuncion]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Asuncion]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Asuncion]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Asuncion]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Asuncion]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Asuncion]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Asuncion]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Atikokan]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Atikokan]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Atikokan]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Atikokan]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Atikokan]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Atikokan]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Atikokan]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Atikokan]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Bahia]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bahia]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bahia]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1377,15 +1395,19 @@ test.datetimetester.ZoneInfoTest[America/Bahia]_Pure.test_system_transitions @ d test.datetimetester.ZoneInfoTest[America/Bahia_Banderas]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bahia_Banderas]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bahia_Banderas]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Bahia_Banderas]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Bahia_Banderas]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bahia_Banderas]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bahia_Banderas]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Bahia_Banderas]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Barbados]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Barbados]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Barbados]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Barbados]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Barbados]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Barbados]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Barbados]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Barbados]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Belem]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Belem]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Belem]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1395,15 +1417,17 @@ test.datetimetester.ZoneInfoTest[America/Belem]_Pure.test_system_transitions @ d test.datetimetester.ZoneInfoTest[America/Belize]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Belize]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Belize]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Belize]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Belize]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Belize]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Belize]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Belize]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Blanc-Sablon]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Blanc-Sablon]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Blanc-Sablon]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Blanc-Sablon]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Blanc-Sablon]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Blanc-Sablon]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Blanc-Sablon]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Blanc-Sablon]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Boa_Vista]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Boa_Vista]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Boa_Vista]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1412,10 +1436,10 @@ test.datetimetester.ZoneInfoTest[America/Boa_Vista]_Pure.test_gaps @ darwin-arm6 test.datetimetester.ZoneInfoTest[America/Boa_Vista]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bogota]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bogota]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Bogota]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Bogota]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bogota]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Bogota]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Bogota]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Bogota]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Boise]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Boise]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Boise]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1424,10 +1448,10 @@ test.datetimetester.ZoneInfoTest[America/Boise]_Pure.test_gaps @ darwin-arm64,da test.datetimetester.ZoneInfoTest[America/Boise]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cambridge_Bay]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cambridge_Bay]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Cambridge_Bay]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Cambridge_Bay]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cambridge_Bay]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cambridge_Bay]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Cambridge_Bay]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Cambridge_Bay]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Campo_Grande]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Campo_Grande]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Campo_Grande]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1437,9 +1461,11 @@ test.datetimetester.ZoneInfoTest[America/Campo_Grande]_Pure.test_system_transiti test.datetimetester.ZoneInfoTest[America/Cancun]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cancun]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cancun]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Cancun]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Cancun]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cancun]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cancun]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Cancun]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Caracas]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Caracas]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Caracas]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1467,15 +1493,19 @@ test.datetimetester.ZoneInfoTest[America/Chicago]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[America/Chihuahua]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Chihuahua]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Chihuahua]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Chihuahua]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Chihuahua]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Chihuahua]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Chihuahua]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Fast.test_folds @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Fast.test_gaps @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Chihuahua]_Pure.test_system_transitions @ darwin-arm64 +test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Pure.test_folds @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Pure.test_gaps @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Fast.test_system_transitions @ darwin-arm64 +test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Ciudad_Juarez]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Costa_Rica]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Costa_Rica]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Costa_Rica]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1490,10 +1520,10 @@ test.datetimetester.ZoneInfoTest[America/Coyhaique]_Pure.test_gaps @ darwin-x86_ test.datetimetester.ZoneInfoTest[America/Coyhaique]_Pure.test_system_transitions @ darwin-x86_64,linux-aarch64-github,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Creston]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Creston]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Creston]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Creston]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Creston]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Creston]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Creston]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Creston]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Cuiaba]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cuiaba]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Cuiaba]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1502,10 +1532,10 @@ test.datetimetester.ZoneInfoTest[America/Cuiaba]_Pure.test_gaps @ darwin-arm64,d test.datetimetester.ZoneInfoTest[America/Cuiaba]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Curacao]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Curacao]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Curacao]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Curacao]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Curacao]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Curacao]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Curacao]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Curacao]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Danmarkshavn]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Danmarkshavn]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Danmarkshavn]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1515,9 +1545,11 @@ test.datetimetester.ZoneInfoTest[America/Danmarkshavn]_Pure.test_system_transiti test.datetimetester.ZoneInfoTest[America/Dawson]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Dawson]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Dawson]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Dawson]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Dawson]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Dawson]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Dawson]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Dawson]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Dawson_Creek]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Dawson_Creek]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Dawson_Creek]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1545,9 +1577,11 @@ test.datetimetester.ZoneInfoTest[America/Dominica]_Pure.test_system_transitions test.datetimetester.ZoneInfoTest[America/Edmonton]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Edmonton]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Edmonton]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Edmonton]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Edmonton]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Edmonton]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Edmonton]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Edmonton]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Eirunepe]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Eirunepe]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Eirunepe]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1617,9 +1651,11 @@ test.datetimetester.ZoneInfoTest[America/Guayaquil]_Pure.test_system_transitions test.datetimetester.ZoneInfoTest[America/Guyana]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Guyana]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Guyana]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Guyana]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Guyana]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Guyana]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Guyana]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Guyana]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Halifax]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Halifax]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Halifax]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1635,9 +1671,11 @@ test.datetimetester.ZoneInfoTest[America/Havana]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[America/Hermosillo]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Hermosillo]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Hermosillo]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Hermosillo]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Hermosillo]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Hermosillo]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Hermosillo]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Hermosillo]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Indiana/Indianapolis]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Indiana/Indianapolis]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Indiana/Indianapolis]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1665,9 +1703,11 @@ test.datetimetester.ZoneInfoTest[America/Indiana/Petersburg]_Pure.test_system_tr test.datetimetester.ZoneInfoTest[America/Indiana/Tell_City]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Indiana/Tell_City]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Indiana/Tell_City]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Indiana/Tell_City]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Indiana/Tell_City]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Indiana/Tell_City]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Indiana/Tell_City]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Indiana/Tell_City]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Indiana/Vevay]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Indiana/Vevay]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Indiana/Vevay]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1688,16 +1728,16 @@ test.datetimetester.ZoneInfoTest[America/Indiana/Winamac]_Pure.test_gaps @ darwi test.datetimetester.ZoneInfoTest[America/Indiana/Winamac]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Inuvik]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Inuvik]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Inuvik]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Inuvik]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Inuvik]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Inuvik]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Inuvik]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Inuvik]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Iqaluit]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Iqaluit]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Iqaluit]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Iqaluit]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Iqaluit]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Iqaluit]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Iqaluit]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Iqaluit]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Jamaica]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Jamaica]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Jamaica]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1713,9 +1753,11 @@ test.datetimetester.ZoneInfoTest[America/Juneau]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[America/Kentucky/Louisville]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kentucky/Louisville]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kentucky/Louisville]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Kentucky/Louisville]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Kentucky/Louisville]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kentucky/Louisville]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kentucky/Louisville]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Kentucky/Louisville]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Kentucky/Monticello]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kentucky/Monticello]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kentucky/Monticello]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1724,10 +1766,10 @@ test.datetimetester.ZoneInfoTest[America/Kentucky/Monticello]_Pure.test_gaps @ d test.datetimetester.ZoneInfoTest[America/Kentucky/Monticello]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kralendijk]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kralendijk]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Kralendijk]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Kralendijk]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Kralendijk]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Kralendijk]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Kralendijk]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Kralendijk]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/La_Paz]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/La_Paz]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/La_Paz]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1748,10 +1790,10 @@ test.datetimetester.ZoneInfoTest[America/Los_Angeles]_Pure.test_gaps @ darwin-ar test.datetimetester.ZoneInfoTest[America/Los_Angeles]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Lower_Princes]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Lower_Princes]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Lower_Princes]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Lower_Princes]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Lower_Princes]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Lower_Princes]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Lower_Princes]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[America/Lower_Princes]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Maceio]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Maceio]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Maceio]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1791,9 +1833,11 @@ test.datetimetester.ZoneInfoTest[America/Matamoros]_Pure.test_system_transitions test.datetimetester.ZoneInfoTest[America/Mazatlan]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Mazatlan]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Mazatlan]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Mazatlan]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Mazatlan]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Mazatlan]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Mazatlan]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Mazatlan]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Menominee]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Menominee]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Menominee]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1803,9 +1847,11 @@ test.datetimetester.ZoneInfoTest[America/Menominee]_Pure.test_system_transitions test.datetimetester.ZoneInfoTest[America/Merida]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Merida]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Merida]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Merida]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Merida]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Merida]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Merida]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Merida]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Metlakatla]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Metlakatla]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Metlakatla]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1815,9 +1861,11 @@ test.datetimetester.ZoneInfoTest[America/Metlakatla]_Pure.test_system_transition test.datetimetester.ZoneInfoTest[America/Mexico_City]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Mexico_City]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Mexico_City]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Mexico_City]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Mexico_City]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Mexico_City]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Mexico_City]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Mexico_City]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Miquelon]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Miquelon]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Miquelon]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1832,10 +1880,10 @@ test.datetimetester.ZoneInfoTest[America/Moncton]_Pure.test_gaps @ darwin-arm64, test.datetimetester.ZoneInfoTest[America/Moncton]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Monterrey]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Monterrey]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Monterrey]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Monterrey]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Monterrey]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Monterrey]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Monterrey]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Monterrey]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Montevideo]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Montevideo]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Montevideo]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1860,10 +1908,6 @@ test.datetimetester.ZoneInfoTest[America/New_York]_Fast.test_system_transitions test.datetimetester.ZoneInfoTest[America/New_York]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/New_York]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/New_York]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Nipigon]_Fast.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Nipigon]_Fast.test_gaps @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Nipigon]_Pure.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Nipigon]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Nome]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Nome]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Nome]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1896,28 +1940,26 @@ test.datetimetester.ZoneInfoTest[America/North_Dakota/New_Salem]_Pure.test_gaps test.datetimetester.ZoneInfoTest[America/North_Dakota/New_Salem]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Nuuk]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Nuuk]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Nuuk]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Nuuk]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64-github,linux-x86_64-github !test.datetimetester.ZoneInfoTest[America/Nuuk]_Fast.test_system_transitions @ darwin-x86_64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[America/Nuuk]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Nuuk]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Nuuk]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64-github,linux-x86_64-github !test.datetimetester.ZoneInfoTest[America/Nuuk]_Pure.test_system_transitions @ darwin-x86_64,linux-aarch64,linux-x86_64 -test.datetimetester.ZoneInfoTest[America/Nuuk]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Ojinaga]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Ojinaga]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Ojinaga]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Ojinaga]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Ojinaga]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Ojinaga]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Ojinaga]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Ojinaga]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Panama]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Panama]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Panama]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Panama]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Panama]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Panama]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Pangnirtung]_Fast.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Pangnirtung]_Fast.test_gaps @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Pangnirtung]_Pure.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Pangnirtung]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Paramaribo]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Paramaribo]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Paramaribo]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1956,20 +1998,16 @@ test.datetimetester.ZoneInfoTest[America/Puerto_Rico]_Pure.test_gaps @ darwin-ar test.datetimetester.ZoneInfoTest[America/Puerto_Rico]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Punta_Arenas]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Punta_Arenas]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Punta_Arenas]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Punta_Arenas]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Punta_Arenas]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Punta_Arenas]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Punta_Arenas]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Rainy_River]_Fast.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Rainy_River]_Fast.test_gaps @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Rainy_River]_Pure.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Rainy_River]_Pure.test_gaps @ darwin-arm64 +test.datetimetester.ZoneInfoTest[America/Punta_Arenas]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Rankin_Inlet]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Rankin_Inlet]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Rankin_Inlet]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Rankin_Inlet]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Rankin_Inlet]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Rankin_Inlet]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Rankin_Inlet]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Rankin_Inlet]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Recife]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Recife]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Recife]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -1984,10 +2022,10 @@ test.datetimetester.ZoneInfoTest[America/Regina]_Pure.test_gaps @ darwin-arm64,d test.datetimetester.ZoneInfoTest[America/Regina]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Resolute]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Resolute]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Resolute]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Resolute]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Resolute]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Resolute]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Resolute]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Resolute]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Rio_Branco]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Rio_Branco]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Rio_Branco]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2002,10 +2040,10 @@ test.datetimetester.ZoneInfoTest[America/Santarem]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Santarem]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Santiago]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Santiago]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Santiago]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Santiago]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Santiago]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Santiago]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Santiago]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Santiago]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Santo_Domingo]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Santo_Domingo]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Santo_Domingo]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2021,9 +2059,11 @@ test.datetimetester.ZoneInfoTest[America/Sao_Paulo]_Pure.test_system_transitions test.datetimetester.ZoneInfoTest[America/Scoresbysund]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Scoresbysund]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Scoresbysund]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Scoresbysund]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Scoresbysund]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Scoresbysund]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Scoresbysund]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Scoresbysund]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Sitka]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Sitka]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Sitka]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2084,22 +2124,22 @@ test.datetimetester.ZoneInfoTest[America/Thule]_Fast.test_system_transitions @ d test.datetimetester.ZoneInfoTest[America/Thule]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Thule]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Thule]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Thunder_Bay]_Fast.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Thunder_Bay]_Fast.test_gaps @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Thunder_Bay]_Pure.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Thunder_Bay]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Tijuana]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Tijuana]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Tijuana]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Tijuana]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Tijuana]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Tijuana]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Tijuana]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Tijuana]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Toronto]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Toronto]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Toronto]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Toronto]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Toronto]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Toronto]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Toronto]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Toronto]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Tortola]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Tortola]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Tortola]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2109,15 +2149,17 @@ test.datetimetester.ZoneInfoTest[America/Tortola]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[America/Vancouver]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Vancouver]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Vancouver]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Vancouver]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Vancouver]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Vancouver]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Vancouver]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[America/Vancouver]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[America/Whitehorse]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Whitehorse]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Whitehorse]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Whitehorse]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Whitehorse]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Whitehorse]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Whitehorse]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[America/Whitehorse]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Winnipeg]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Winnipeg]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Winnipeg]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2130,10 +2172,6 @@ test.datetimetester.ZoneInfoTest[America/Yakutat]_Fast.test_system_transitions @ test.datetimetester.ZoneInfoTest[America/Yakutat]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Yakutat]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[America/Yakutat]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[America/Yellowknife]_Fast.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Yellowknife]_Fast.test_gaps @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Yellowknife]_Pure.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[America/Yellowknife]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[Antarctica/Casey]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/Casey]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/Casey]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2148,16 +2186,18 @@ test.datetimetester.ZoneInfoTest[Antarctica/Davis]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[Antarctica/Davis]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/DumontDUrville]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/DumontDUrville]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Antarctica/DumontDUrville]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Antarctica/DumontDUrville]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Antarctica/DumontDUrville]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/DumontDUrville]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Antarctica/DumontDUrville]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Antarctica/DumontDUrville]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Antarctica/Macquarie]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/Macquarie]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/Macquarie]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Antarctica/Macquarie]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Antarctica/Macquarie]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/Macquarie]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/Macquarie]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Antarctica/Macquarie]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Antarctica/Mawson]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/Mawson]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Antarctica/Mawson]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2202,10 +2242,10 @@ test.datetimetester.ZoneInfoTest[Antarctica/Vostok]_Pure.test_gaps @ darwin-arm6 test.datetimetester.ZoneInfoTest[Antarctica/Vostok]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Arctic/Longyearbyen]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Arctic/Longyearbyen]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Arctic/Longyearbyen]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Arctic/Longyearbyen]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Arctic/Longyearbyen]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Arctic/Longyearbyen]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Arctic/Longyearbyen]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Arctic/Longyearbyen]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Asia/Aden]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Aden]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Aden]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2220,10 +2260,10 @@ test.datetimetester.ZoneInfoTest[Asia/Almaty]_Pure.test_gaps @ darwin-arm64,darw test.datetimetester.ZoneInfoTest[Asia/Almaty]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Amman]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Amman]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Asia/Amman]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Asia/Amman]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Amman]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Amman]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Asia/Amman]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Asia/Amman]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Anadyr]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Anadyr]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Anadyr]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2320,10 +2360,10 @@ test.datetimetester.ZoneInfoTest[Asia/Colombo]_Pure.test_gaps @ darwin-arm64,dar test.datetimetester.ZoneInfoTest[Asia/Colombo]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Damascus]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Damascus]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Asia/Damascus]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Asia/Damascus]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Damascus]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Damascus]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Asia/Damascus]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Asia/Damascus]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Dhaka]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Dhaka]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Dhaka]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2357,21 +2397,27 @@ test.datetimetester.ZoneInfoTest[Asia/Famagusta]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[Asia/Gaza]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Gaza]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Gaza]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Gaza]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Gaza]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Gaza]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Gaza]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Gaza]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Hebron]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Hebron]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Hebron]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Hebron]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Hebron]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Hebron]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Hebron]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Hebron]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Ho_Chi_Minh]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Ho_Chi_Minh]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Ho_Chi_Minh]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Ho_Chi_Minh]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Ho_Chi_Minh]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Ho_Chi_Minh]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Ho_Chi_Minh]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Ho_Chi_Minh]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Hong_Kong]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Hong_Kong]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Hong_Kong]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2405,9 +2451,11 @@ test.datetimetester.ZoneInfoTest[Asia/Jayapura]_Pure.test_system_transitions @ d test.datetimetester.ZoneInfoTest[Asia/Jerusalem]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Jerusalem]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Jerusalem]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Jerusalem]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Jerusalem]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Jerusalem]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Jerusalem]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Jerusalem]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Kabul]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kabul]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kabul]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2452,10 +2500,10 @@ test.datetimetester.ZoneInfoTest[Asia/Krasnoyarsk]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Krasnoyarsk]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kuala_Lumpur]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kuala_Lumpur]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Asia/Kuala_Lumpur]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Asia/Kuala_Lumpur]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kuala_Lumpur]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kuala_Lumpur]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Asia/Kuala_Lumpur]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Asia/Kuala_Lumpur]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kuching]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kuching]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Kuching]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2489,9 +2537,11 @@ test.datetimetester.ZoneInfoTest[Asia/Makassar]_Pure.test_system_transitions @ d test.datetimetester.ZoneInfoTest[Asia/Manila]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Manila]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Manila]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Manila]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Manila]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Manila]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Manila]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Manila]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Muscat]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Muscat]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Muscat]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2596,10 +2646,10 @@ test.datetimetester.ZoneInfoTest[Asia/Shanghai]_Pure.test_gaps @ darwin-arm64,da test.datetimetester.ZoneInfoTest[Asia/Shanghai]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Singapore]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Singapore]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Asia/Singapore]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Asia/Singapore]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Singapore]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Singapore]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Asia/Singapore]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Asia/Singapore]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Srednekolymsk]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Srednekolymsk]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Srednekolymsk]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2627,9 +2677,11 @@ test.datetimetester.ZoneInfoTest[Asia/Tbilisi]_Pure.test_system_transitions @ da test.datetimetester.ZoneInfoTest[Asia/Tehran]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Tehran]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Tehran]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Tehran]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Tehran]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Tehran]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Tehran]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Asia/Tehran]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Asia/Thimphu]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Thimphu]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Asia/Thimphu]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2705,9 +2757,11 @@ test.datetimetester.ZoneInfoTest[Asia/Yerevan]_Pure.test_system_transitions @ da test.datetimetester.ZoneInfoTest[Atlantic/Azores]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Azores]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Azores]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Atlantic/Azores]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Atlantic/Azores]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Azores]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Azores]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Atlantic/Azores]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Atlantic/Bermuda]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Bermuda]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Bermuda]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2735,15 +2789,17 @@ test.datetimetester.ZoneInfoTest[Atlantic/Faroe]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[Atlantic/Madeira]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Madeira]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Madeira]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Atlantic/Madeira]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Atlantic/Madeira]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Madeira]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Madeira]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Atlantic/Madeira]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Atlantic/Reykjavik]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Reykjavik]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Atlantic/Reykjavik]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Atlantic/Reykjavik]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Atlantic/Reykjavik]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/Reykjavik]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Atlantic/Reykjavik]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Atlantic/Reykjavik]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Atlantic/South_Georgia]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/South_Georgia]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Atlantic/South_Georgia]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2765,45 +2821,59 @@ test.datetimetester.ZoneInfoTest[Atlantic/Stanley]_Pure.test_system_transitions test.datetimetester.ZoneInfoTest[Australia/Adelaide]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Adelaide]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Adelaide]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Adelaide]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Adelaide]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Adelaide]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Adelaide]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Adelaide]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Brisbane]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Brisbane]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Brisbane]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Brisbane]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Brisbane]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Brisbane]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Brisbane]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Brisbane]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Broken_Hill]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Broken_Hill]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Broken_Hill]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Broken_Hill]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Broken_Hill]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Broken_Hill]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Broken_Hill]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Broken_Hill]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Darwin]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Darwin]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Darwin]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Darwin]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Darwin]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Darwin]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Darwin]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Darwin]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Eucla]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Eucla]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Eucla]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Eucla]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Eucla]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Eucla]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Eucla]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Eucla]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Hobart]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Hobart]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Hobart]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Hobart]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Hobart]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Hobart]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Hobart]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Hobart]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Lindeman]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Lindeman]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Lindeman]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Lindeman]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Lindeman]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Lindeman]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Lindeman]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Lindeman]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Lord_Howe]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Lord_Howe]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Lord_Howe]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2813,27 +2883,33 @@ test.datetimetester.ZoneInfoTest[Australia/Lord_Howe]_Pure.test_system_transitio test.datetimetester.ZoneInfoTest[Australia/Melbourne]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Melbourne]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Melbourne]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Melbourne]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Melbourne]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Melbourne]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Melbourne]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Melbourne]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Perth]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Perth]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Perth]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Perth]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Perth]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Perth]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Perth]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Perth]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Sydney]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Sydney]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Sydney]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Sydney]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Australia/Sydney]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Sydney]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Australia/Sydney]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Australia/Sydney]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Amsterdam]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Amsterdam]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Amsterdam]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Amsterdam]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Amsterdam]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Amsterdam]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Amsterdam]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Amsterdam]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Andorra]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Andorra]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Andorra]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2885,9 +2961,11 @@ test.datetimetester.ZoneInfoTest[Europe/Bucharest]_Pure.test_system_transitions test.datetimetester.ZoneInfoTest[Europe/Budapest]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Budapest]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Budapest]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Budapest]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Budapest]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Budapest]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Budapest]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Budapest]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Busingen]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Busingen]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Busingen]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2896,16 +2974,18 @@ test.datetimetester.ZoneInfoTest[Europe/Busingen]_Pure.test_gaps @ darwin-arm64, test.datetimetester.ZoneInfoTest[Europe/Busingen]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Fast.test_system_transitions @ linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Fast.test_system_transitions @ linux-aarch64-github test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Pure.test_system_transitions @ linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Chisinau]_Pure.test_system_transitions @ linux-aarch64-github test.datetimetester.ZoneInfoTest[Europe/Copenhagen]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Copenhagen]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Copenhagen]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Copenhagen]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Copenhagen]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Copenhagen]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Copenhagen]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Copenhagen]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Dublin]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Dublin]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Dublin]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -2939,9 +3019,11 @@ test.datetimetester.ZoneInfoTest[Europe/Isle_of_Man]_Pure.test_system_transition test.datetimetester.ZoneInfoTest[Europe/Istanbul]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Istanbul]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Istanbul]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Istanbul]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Istanbul]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Istanbul]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Istanbul]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Istanbul]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Jersey]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Jersey]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Jersey]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64 @@ -2951,31 +3033,31 @@ test.datetimetester.ZoneInfoTest[Europe/Jersey]_Pure.test_system_transitions @ d test.datetimetester.ZoneInfoTest[Europe/Kaliningrad]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kaliningrad]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kaliningrad]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Kaliningrad]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Kaliningrad]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kaliningrad]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kaliningrad]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Kiev]_Fast.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Kiev]_Fast.test_gaps @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Kiev]_Pure.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Kiev]_Pure.test_gaps @ darwin-arm64 +!test.datetimetester.ZoneInfoTest[Europe/Kaliningrad]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Kirov]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kirov]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kirov]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kirov]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kirov]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Kirov]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Fast.test_folds @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Fast.test_gaps @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Fast.test_system_transitions @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Pure.test_folds @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Pure.test_gaps @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Pure.test_system_transitions @ darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Kyiv]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Lisbon]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Lisbon]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Lisbon]_Fast.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Lisbon]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Lisbon]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Lisbon]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Lisbon]_Pure.test_system_transitions @ linux-aarch64-github,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Lisbon]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Ljubljana]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Ljubljana]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Ljubljana]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64 @@ -2990,10 +3072,10 @@ test.datetimetester.ZoneInfoTest[Europe/London]_Pure.test_gaps @ darwin-arm64,da test.datetimetester.ZoneInfoTest[Europe/London]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Luxembourg]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Luxembourg]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Luxembourg]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Luxembourg]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Luxembourg]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Luxembourg]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Luxembourg]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Luxembourg]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Madrid]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Madrid]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Madrid]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3020,10 +3102,10 @@ test.datetimetester.ZoneInfoTest[Europe/Minsk]_Pure.test_gaps @ darwin-arm64,dar test.datetimetester.ZoneInfoTest[Europe/Minsk]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Monaco]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Monaco]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Monaco]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Monaco]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Monaco]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Monaco]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Monaco]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Monaco]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Moscow]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Moscow]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Moscow]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3032,10 +3114,10 @@ test.datetimetester.ZoneInfoTest[Europe/Moscow]_Pure.test_gaps @ darwin-arm64,da test.datetimetester.ZoneInfoTest[Europe/Moscow]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Oslo]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Oslo]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Oslo]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Oslo]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Oslo]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Oslo]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Oslo]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Europe/Oslo]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Europe/Paris]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Paris]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Paris]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3092,10 +3174,10 @@ test.datetimetester.ZoneInfoTest[Europe/Saratov]_Pure.test_gaps @ darwin-arm64,d test.datetimetester.ZoneInfoTest[Europe/Saratov]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Simferopol]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Simferopol]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Simferopol]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Simferopol]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Simferopol]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Simferopol]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Simferopol]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Europe/Simferopol]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Skopje]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Skopje]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Skopje]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64 @@ -3132,10 +3214,6 @@ test.datetimetester.ZoneInfoTest[Europe/Ulyanovsk]_Fast.test_system_transitions test.datetimetester.ZoneInfoTest[Europe/Ulyanovsk]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Ulyanovsk]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Ulyanovsk]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Europe/Uzhgorod]_Fast.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Uzhgorod]_Fast.test_gaps @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Uzhgorod]_Pure.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Uzhgorod]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Vaduz]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Vaduz]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Vaduz]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3151,9 +3229,11 @@ test.datetimetester.ZoneInfoTest[Europe/Vatican]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[Europe/Vienna]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Vienna]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Vienna]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Vienna]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Vienna]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Vienna]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Vienna]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Europe/Vienna]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Vilnius]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Vilnius]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Vilnius]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3178,10 +3258,6 @@ test.datetimetester.ZoneInfoTest[Europe/Zagreb]_Fast.test_system_transitions @ d test.datetimetester.ZoneInfoTest[Europe/Zagreb]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Zagreb]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Zagreb]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-x86_64 -test.datetimetester.ZoneInfoTest[Europe/Zaporozhye]_Fast.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Zaporozhye]_Fast.test_gaps @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Zaporozhye]_Pure.test_folds @ darwin-arm64 -test.datetimetester.ZoneInfoTest[Europe/Zaporozhye]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[Europe/Zurich]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Zurich]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Europe/Zurich]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3191,9 +3267,11 @@ test.datetimetester.ZoneInfoTest[Europe/Zurich]_Pure.test_system_transitions @ d test.datetimetester.ZoneInfoTest[Indian/Antananarivo]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Antananarivo]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Antananarivo]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Indian/Antananarivo]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Indian/Antananarivo]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Antananarivo]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Antananarivo]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +!test.datetimetester.ZoneInfoTest[Indian/Antananarivo]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Indian/Chagos]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Chagos]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Chagos]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3215,9 +3293,11 @@ test.datetimetester.ZoneInfoTest[Indian/Cocos]_Pure.test_system_transitions @ da test.datetimetester.ZoneInfoTest[Indian/Comoro]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Comoro]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Comoro]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Indian/Comoro]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Indian/Comoro]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Comoro]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Comoro]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Indian/Comoro]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Indian/Kerguelen]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Kerguelen]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Kerguelen]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3245,9 +3325,11 @@ test.datetimetester.ZoneInfoTest[Indian/Mauritius]_Pure.test_system_transitions test.datetimetester.ZoneInfoTest[Indian/Mayotte]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Mayotte]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Mayotte]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Indian/Mayotte]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Indian/Mayotte]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Mayotte]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Mayotte]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Indian/Mayotte]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Indian/Reunion]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Reunion]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Indian/Reunion]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3257,9 +3339,11 @@ test.datetimetester.ZoneInfoTest[Indian/Reunion]_Pure.test_system_transitions @ test.datetimetester.ZoneInfoTest[Pacific/Apia]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Apia]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Apia]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Pacific/Apia]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Apia]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Apia]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Apia]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Pacific/Apia]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Auckland]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Auckland]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Auckland]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3280,22 +3364,24 @@ test.datetimetester.ZoneInfoTest[Pacific/Chatham]_Pure.test_gaps @ darwin-arm64, test.datetimetester.ZoneInfoTest[Pacific/Chatham]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Chuuk]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Chuuk]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Chuuk]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Pacific/Chuuk]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Pacific/Chuuk]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Chuuk]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Chuuk]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Pacific/Chuuk]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Pacific/Easter]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Easter]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Easter]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Pacific/Easter]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Easter]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Easter]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Easter]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Pacific/Easter]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Efate]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Efate]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Efate]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Pacific/Efate]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Efate]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Efate]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Efate]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Pacific/Efate]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Fakaofo]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Fakaofo]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Fakaofo]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3304,10 +3390,10 @@ test.datetimetester.ZoneInfoTest[Pacific/Fakaofo]_Pure.test_gaps @ darwin-arm64, test.datetimetester.ZoneInfoTest[Pacific/Fakaofo]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Fiji]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Fiji]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Fiji]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Pacific/Fiji]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Fiji]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Fiji]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Fiji]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.datetimetester.ZoneInfoTest[Pacific/Fiji]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Funafuti]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Funafuti]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Funafuti]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3370,10 +3456,10 @@ test.datetimetester.ZoneInfoTest[Pacific/Kwajalein]_Pure.test_gaps @ darwin-arm6 test.datetimetester.ZoneInfoTest[Pacific/Kwajalein]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Majuro]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Majuro]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Majuro]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Pacific/Majuro]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Pacific/Majuro]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Majuro]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Majuro]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Pacific/Majuro]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Pacific/Marquesas]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Marquesas]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Marquesas]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3395,15 +3481,19 @@ test.datetimetester.ZoneInfoTest[Pacific/Nauru]_Pure.test_system_transitions @ d test.datetimetester.ZoneInfoTest[Pacific/Niue]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Niue]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Niue]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Pacific/Niue]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Niue]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Niue]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Niue]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Pacific/Niue]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Norfolk]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Norfolk]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Norfolk]_Fast.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Pacific/Norfolk]_Fast.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Norfolk]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Norfolk]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Norfolk]_Pure.test_system_transitions @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.datetimetester.ZoneInfoTest[Pacific/Norfolk]_Pure.test_system_transitions @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Noumea]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Noumea]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Noumea]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -3430,10 +3520,10 @@ test.datetimetester.ZoneInfoTest[Pacific/Pitcairn]_Pure.test_gaps @ darwin-arm64 test.datetimetester.ZoneInfoTest[Pacific/Pitcairn]_Pure.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Pohnpei]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Pohnpei]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Pohnpei]_Fast.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Pacific/Pohnpei]_Fast.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Pacific/Pohnpei]_Pure.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Pohnpei]_Pure.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.datetimetester.ZoneInfoTest[Pacific/Pohnpei]_Pure.test_system_transitions @ linux-aarch64,linux-x86_64 +test.datetimetester.ZoneInfoTest[Pacific/Pohnpei]_Pure.test_system_transitions @ darwin-arm64,linux-aarch64,linux-x86_64 test.datetimetester.ZoneInfoTest[Pacific/Port_Moresby]_Fast.test_folds @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Port_Moresby]_Fast.test_gaps @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.datetimetester.ZoneInfoTest[Pacific/Port_Moresby]_Fast.test_system_transitions @ darwin-arm64,darwin-x86_64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_decorators.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_decorators.txt index daae90d5e6..f53161d778 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_decorators.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_decorators.txt @@ -14,3 +14,4 @@ test.test_decorators.TestDecorators.test_order @ darwin-arm64,linux-aarch64,linu test.test_decorators.TestDecorators.test_single @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_decorators.TestDecorators.test_staticmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_decorators.TestDecorators.test_wrapped_classmethod_inside_classmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_decorators.TestDecorators.test_wrapped_descriptor_inside_classmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_descr.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_descr.txt index e8b7d447df..517c1b3218 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_descr.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_descr.txt @@ -71,9 +71,7 @@ test.test_descr.ClassPropertiesAndMethods.test_python_lists @ darwin-arm64,linux test.test_descr.ClassPropertiesAndMethods.test_qualname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_descr.ClassPropertiesAndMethods.test_qualname_dict @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_descr.ClassPropertiesAndMethods.test_recursive_call @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_descr.ClassPropertiesAndMethods.test_remove_subclass @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -# GR-71917 -!test.test_descr.ClassPropertiesAndMethods.test_remove_subclass @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_descr.ClassPropertiesAndMethods.test_remove_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_descr.ClassPropertiesAndMethods.test_repr_as_str @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_descr.ClassPropertiesAndMethods.test_repr_with_module_str_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_descr.ClassPropertiesAndMethods.test_restored_object_new @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_dictviews.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_dictviews.txt index 7f2f51d783..38e6fd21d9 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_dictviews.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_dictviews.txt @@ -2,7 +2,8 @@ test.test_dictviews.DictSetTest.test_abc_registry @ darwin-arm64,linux-aarch64,l test.test_dictviews.DictSetTest.test_compare_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_dictviews.DictSetTest.test_constructors_not_callable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_dictviews.DictSetTest.test_copy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_dictviews.DictSetTest.test_deeply_nested_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,win32-AMD64,win32-AMD64-github +# Unreliable RecursionError +!test.test_dictviews.DictSetTest.test_deeply_nested_repr test.test_dictviews.DictSetTest.test_dict_items @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_dictviews.DictSetTest.test_dict_keys @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_dictviews.DictSetTest.test_dict_mixed_keys_items @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_except_star.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_except_star.txt index 0555f08655..a88aa5afb1 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_except_star.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_except_star.txt @@ -1,58 +1,59 @@ -test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_break_continue_in_except_star_block_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_break_in_except_star @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_continue_in_except_star_block_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_return_in_except_star_block_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_return_in_except_star_block_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarCleanup.test_sys_exception_restored @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarExceptionGroupSubclass.test_except_star_EG_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarExceptionGroupSubclass.test_falsy_exception_group_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaise.test_raise_handle_all_raise_one_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaise.test_raise_handle_all_raise_one_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaise.test_raise_handle_all_raise_two_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaise.test_raise_handle_all_raise_two_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaise.test_raise_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaise.test_raise_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaiseFrom.test_raise_handle_all_raise_one_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaiseFrom.test_raise_handle_all_raise_one_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaiseFrom.test_raise_handle_all_raise_two_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaiseFrom.test_raise_handle_all_raise_two_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaiseFrom.test_raise_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarRaiseFrom.test_raise_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarReraise.test_reraise_all_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarReraise.test_reraise_all_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarReraise.test_reraise_partial_handle_all_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarReraise.test_reraise_partial_handle_some_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarReraise.test_reraise_partial_handle_some_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarReraise.test_reraise_plain_exception_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarReraise.test_reraise_plain_exception_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarReraise.test_reraise_some_handle_all_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_empty_groups_removed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_exception_group_except_star_Exception_not_wrapped @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_first_match_wins_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_first_match_wins_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_match__supertype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_match_single_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_match_single_type_nested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_match_single_type_partial_match @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_match_type_tuple_nested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_multiple_matches_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_multiple_matches_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_naked_exception_matched_wrapped1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_naked_exception_matched_wrapped2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_nested_except_stars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_nested_in_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_no_match_single_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_plain_exception_not_matched @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStarSplitSemantics.test_singleton_groups_are_kept @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStar_WeirdExceptionGroupSubclass.test_catch_all_unhashable_exception_group_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStar_WeirdExceptionGroupSubclass.test_catch_none_unhashable_exception_group_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStar_WeirdExceptionGroupSubclass.test_catch_some_unhashable_exception_group_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_catch_everything_unhashable_leaf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_catch_nothing_unhashable_leaf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_catch_unhashable_leaf_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_propagate_unhashable_leaf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_reraise_unhashable_leaf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestInvalidExceptStar.test_except_star_ExceptionGroup_is_runtime_error_single @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestInvalidExceptStar.test_except_star_ExceptionGroup_is_runtime_error_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestInvalidExceptStar.test_except_star_invalid_exception_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_except_star.TestInvalidExceptStar.test_mixed_except_and_except_star_is_syntax_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_break_continue_in_except_star_block_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_break_in_except_star @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_continue_in_except_star_block_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_return_in_except_star_block_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestBreakContinueReturnInExceptStarBlock.test_return_in_except_star_block_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarCleanup.test_sys_exception_restored @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarExceptionGroupSubclass.test_except_star_EG_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarExceptionGroupSubclass.test_falsy_exception_group_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaise.test_raise_handle_all_raise_one_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaise.test_raise_handle_all_raise_one_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaise.test_raise_handle_all_raise_two_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaise.test_raise_handle_all_raise_two_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaise.test_raise_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaise.test_raise_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaiseFrom.test_raise_handle_all_raise_one_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaiseFrom.test_raise_handle_all_raise_one_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaiseFrom.test_raise_handle_all_raise_two_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaiseFrom.test_raise_handle_all_raise_two_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaiseFrom.test_raise_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarRaiseFrom.test_raise_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarReraise.test_reraise_all_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarReraise.test_reraise_all_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarReraise.test_reraise_partial_handle_all_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarReraise.test_reraise_partial_handle_some_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarReraise.test_reraise_partial_handle_some_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarReraise.test_reraise_plain_exception_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarReraise.test_reraise_plain_exception_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarReraise.test_reraise_some_handle_all_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_empty_groups_removed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_exception_group_except_star_Exception_not_wrapped @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_first_match_wins_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_first_match_wins_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_match__supertype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_match_single_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_match_single_type_nested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_match_single_type_partial_match @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_match_type_tuple_nested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_multiple_matches_named @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_multiple_matches_unnamed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_naked_exception_matched_wrapped1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_naked_exception_matched_wrapped2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_nested_except_stars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_nested_in_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_no_match_single_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_plain_exception_not_matched @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStarSplitSemantics.test_singleton_groups_are_kept @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdExceptionGroupSubclass.test_catch_all_unhashable_exception_group_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdExceptionGroupSubclass.test_catch_none_unhashable_exception_group_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdExceptionGroupSubclass.test_catch_some_unhashable_exception_group_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdExceptionGroupSubclass.test_reraise_unhashable_eg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_catch_everything_unhashable_leaf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_catch_nothing_unhashable_leaf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_catch_unhashable_leaf_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_propagate_unhashable_leaf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestExceptStar_WeirdLeafExceptions.test_reraise_unhashable_leaf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestInvalidExceptStar.test_except_star_ExceptionGroup_is_runtime_error_single @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestInvalidExceptStar.test_except_star_ExceptionGroup_is_runtime_error_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestInvalidExceptStar.test_except_star_invalid_exception_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_except_star.TestInvalidExceptStar.test_mixed_except_and_except_star_is_syntax_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_exceptions.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_exceptions.txt index 4cf20c2428..01e25d3d5f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_exceptions.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_exceptions.txt @@ -53,10 +53,11 @@ test.test_exceptions.ImportErrorTests.test_reset_attributes @ darwin-arm64,linux test.test_exceptions.NameErrorTests.test_gh_111654 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_exceptions.NameErrorTests.test_issue45826 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_exceptions.NameErrorTests.test_issue45826_focused @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_exceptions.NameErrorTests.test_name_error_has_name @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_exceptions.NameErrorTests.test_name_error_has_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_exceptions.PEP626Tests.test_lineno_after_raise_in_with_exit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_exceptions.PEP626Tests.test_lineno_after_raise_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_exceptions.PEP626Tests.test_lineno_in_finally_normal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github # GR-62195 !test.test_exceptions.PEP626Tests.test_lineno_in_try @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_exceptions.SyntaxErrorTests.test_attributes_new_constructor @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 test.test_exceptions.SyntaxErrorTests.test_attributes_old_constructor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_faulthandler.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_faulthandler.txt index b975281c44..a781cde37e 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_faulthandler.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_faulthandler.txt @@ -1,7 +1,7 @@ test.test_faulthandler.FaultHandlerTests.test_cancel_later_without_dump_traceback_later @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_faulthandler.FaultHandlerTests.test_disable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +# Disabled since signaling isn't stable during parallel tests +!test.test_faulthandler.FaultHandlerTests.test_disable test.test_faulthandler.FaultHandlerTests.test_disabled_by_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_faulthandler.FaultHandlerTests.test_is_enabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# Disabled since signaling isn't stable during parallel tests !test.test_faulthandler.FaultHandlerTests.test_sigbus !test.test_faulthandler.FaultHandlerTests.test_sigill diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_file.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_file.txt index 90e04569ff..a40576f794 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_file.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_file.txt @@ -4,6 +4,7 @@ test.test_file.CAutoFileTests.testMethods @ darwin-arm64,linux-aarch64,linux-aar test.test_file.CAutoFileTests.testReadWhenWriting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.CAutoFileTests.testReadinto @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.CAutoFileTests.testReadinto_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_file.CAutoFileTests.testWeakRefs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.CAutoFileTests.testWritelinesIntegers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.CAutoFileTests.testWritelinesIntegersUserList @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.CAutoFileTests.testWritelinesNonString @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -19,6 +20,7 @@ test.test_file.PyAutoFileTests.testMethods @ darwin-arm64,linux-aarch64,linux-aa test.test_file.PyAutoFileTests.testReadWhenWriting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.PyAutoFileTests.testReadinto @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.PyAutoFileTests.testReadinto_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_file.PyAutoFileTests.testWeakRefs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.PyAutoFileTests.testWritelinesIntegers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.PyAutoFileTests.testWritelinesIntegersUserList @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_file.PyAutoFileTests.testWritelinesNonString @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_fractions.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_fractions.txt index 5fb058d704..04b9a4885d 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_fractions.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_fractions.txt @@ -39,5 +39,6 @@ test.test_fractions.FractionTest.test_format_f_presentation_type @ darwin-arm64, test.test_fractions.FractionTest.test_format_g_presentation_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_fractions.FractionTest.test_format_no_presentation_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_fractions.FractionTest.test_int_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_fractions.FractionTest.test_invalid_formats @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_fractions.FractionTest.test_is_integer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_fractions.FractionTest.test_slots @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_functools.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_functools.txt index 64282de7b6..2d699b4097 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_functools.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_functools.txt @@ -36,7 +36,7 @@ test.test_functools.TestLRUC.test_lru_cache_threaded @ darwin-arm64,linux-aarch6 test.test_functools.TestLRUC.test_lru_cache_threaded2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUC.test_lru_cache_threaded3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUC.test_lru_cache_typed_is_not_recursive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_functools.TestLRUC.test_lru_cache_weakrefable @ darwin-arm64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_functools.TestLRUC.test_lru_cache_weakrefable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUC.test_lru_hash_only_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUC.test_lru_method @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUC.test_lru_no_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -64,7 +64,7 @@ test.test_functools.TestLRUPy.test_lru_cache_threaded @ darwin-arm64,linux-aarch test.test_functools.TestLRUPy.test_lru_cache_threaded2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUPy.test_lru_cache_threaded3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUPy.test_lru_cache_typed_is_not_recursive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_functools.TestLRUPy.test_lru_cache_weakrefable @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_functools.TestLRUPy.test_lru_cache_weakrefable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUPy.test_lru_hash_only_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUPy.test_lru_method @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_functools.TestLRUPy.test_lru_no_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_genericalias.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_genericalias.txt index cb4b70231a..bd8be2e537 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_genericalias.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_genericalias.txt @@ -18,6 +18,7 @@ test.test_genericalias.BaseTest.test_pickle @ darwin-arm64,linux-aarch64,linux-a test.test_genericalias.BaseTest.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.BaseTest.test_subclassing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.BaseTest.test_subclassing_types_genericalias @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_genericalias.BaseTest.test_subscriptable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.BaseTest.test_type_generic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.BaseTest.test_type_subclass_generic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.BaseTest.test_unbound_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -25,5 +26,6 @@ test.test_genericalias.BaseTest.test_union @ darwin-arm64,linux-aarch64,linux-aa test.test_genericalias.BaseTest.test_union_generic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.BaseTest.test_unpack @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.BaseTest.test_unsubscriptable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_genericalias.BaseTest.test_weakref @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.TypeIterationTests.test_cannot_iterate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_genericalias.TypeIterationTests.test_is_not_instance_of_iterable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_glob.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_glob.txt index 5c18534d21..fa5b10b177 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_glob.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_glob.txt @@ -12,5 +12,4 @@ test.test_glob.GlobTests.test_glob_one_directory @ darwin-arm64,linux-x86_64,lin test.test_glob.GlobTests.test_glob_symlinks @ darwin-arm64,linux-x86_64,linux-x86_64-github test.test_glob.GlobTests.test_hidden_glob @ darwin-arm64,linux-x86_64,linux-x86_64-github test.test_glob.GlobTests.test_recursive_glob @ darwin-arm64,linux-x86_64,linux-x86_64-github -# Transiently fails at line 395 -!test.test_glob.SymlinkLoopGlobTests.test_selflink +test.test_glob.SymlinkLoopGlobTests.test_selflink @ darwin-arm64,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_hashlib.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_hashlib.txt index d3492eb3a4..2ca69b0e42 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_hashlib.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_hashlib.txt @@ -27,13 +27,13 @@ test.test_hashlib.HashLibTestCase.test_case_sha384_1 @ darwin-arm64,linux-aarch6 test.test_hashlib.HashLibTestCase.test_case_sha384_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_hashlib.HashLibTestCase.test_case_sha384_3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_hashlib.HashLibTestCase.test_case_sha3_224_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_hashlib.HashLibTestCase.test_case_sha3_224_vector @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_hashlib.HashLibTestCase.test_case_sha3_224_vector @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_hashlib.HashLibTestCase.test_case_sha3_256_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_hashlib.HashLibTestCase.test_case_sha3_256_vector @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_hashlib.HashLibTestCase.test_case_sha3_256_vector @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_hashlib.HashLibTestCase.test_case_sha3_384_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_hashlib.HashLibTestCase.test_case_sha3_384_vector @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_hashlib.HashLibTestCase.test_case_sha3_384_vector @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_hashlib.HashLibTestCase.test_case_sha3_512_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_hashlib.HashLibTestCase.test_case_sha3_512_vector @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_hashlib.HashLibTestCase.test_case_sha3_512_vector @ darwin-arm64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_hashlib.HashLibTestCase.test_case_sha512_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_hashlib.HashLibTestCase.test_case_sha512_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_hashlib.HashLibTestCase.test_case_sha512_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_httpservers.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_httpservers.txt index 6e515c0007..7f0117275a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_httpservers.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_httpservers.txt @@ -38,22 +38,23 @@ test.test_httpservers.BaseHTTPServerTestCase.test_version_invalid @ darwin-arm64 test.test_httpservers.BaseHTTPServerTestCase.test_version_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_httpservers.BaseHTTPServerTestCase.test_version_none_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_httpservers.BaseHTTPServerTestCase.test_version_signs_and_underscores @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_httpservers.CGIHTTPServerTestCase.test_accept @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_authorization @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_cgi_path_in_sub_directories @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_headers_and_content @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_invaliduri @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_issue19435 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_nested_cgi_path_issue21323 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_no_leading_slash @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_os_environ_is_not_altered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_post @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_query_with_continuous_slashes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_query_with_multiple_question_mark @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_url_collapse_path @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_httpservers.CGIHTTPServerTestCase.test_urlquote_decoding_in_cgi_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_httpservers.CGIHTTPServerTestCase.test_accept @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_authorization @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_cgi_path_in_sub_directories @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_headers_and_content @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_invaliduri @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_issue19435 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_nested_cgi_path_issue21323 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_no_leading_slash @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_os_environ_is_not_altered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_post @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_query_with_continuous_slashes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_query_with_multiple_question_mark @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_url_collapse_path @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_httpservers.CGIHTTPServerTestCase.test_urlquote_decoding_in_cgi_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_httpservers.MiscTestCase.test_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_httpservers.RequestHandlerLoggingTestCase.test_err @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_httpservers.RequestHandlerLoggingTestCase.test_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_httpservers.ScriptTestCase.test_server_test_ipv4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_httpservers.ScriptTestCase.test_server_test_ipv6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_httpservers.ScriptTestCase.test_server_test_localhost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_import.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_import.txt index 47db6bac5c..5b0fd228aa 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_import.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_import.txt @@ -48,8 +48,7 @@ test.test_import.ImportTracebackTests.test_syntax_error @ darwin-arm64,linux-aar test.test_import.OverridingImportBuiltinTests.test_override_builtin @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_import.PathsTests.test_trailing_slash @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_import.PycRewritingTests.test_basics @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71867 -!test.test_import.PycRewritingTests.test_incorrect_code_name @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_import.PycRewritingTests.test_incorrect_code_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_import.PycRewritingTests.test_module_without_source @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_import.PycacheTests.test___cached__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_import.PycacheTests.test___cached___legacy_pyc @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_inspect.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_inspect.txt index f3b355b98b..538bc0a6fe 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_inspect.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_inspect.txt @@ -22,7 +22,7 @@ test.test_inspect.test_inspect.TestBuggyCases.test_multiple_children_classes @ d test.test_inspect.test_inspect.TestBuggyCases.test_nested_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestBuggyCases.test_nested_class_definition @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestBuggyCases.test_nested_class_definition_indented_string @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_inspect.test_inspect.TestBuggyCases.test_nested_class_definition_inside_async_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_inspect.test_inspect.TestBuggyCases.test_nested_class_definition_inside_async_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_inspect.test_inspect.TestBuggyCases.test_nested_class_definition_inside_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestBuggyCases.test_nested_func @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestBuggyCases.test_one_liner_dedent_non_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -62,17 +62,16 @@ test.test_inspect.test_inspect.TestClassesAndFunctions.test_pep_695_generic_meth test.test_inspect.test_inspect.TestClassesAndFunctions.test_pep_695_generic_method_with_future_annotations_name_clash_with_global_and_local_vars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestClassesAndFunctions.test_pep_695_generic_method_with_future_annotations_name_clash_with_global_vars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestClassesAndFunctions.test_pep_695_generics_with_future_annotations_nested_in_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71889 -!test.test_inspect.test_inspect.TestComplexDecorator.test_parens_in_decorator @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -!test.test_inspect.test_inspect.TestDecorators.test_decorator_with_lambda @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -!test.test_inspect.test_inspect.TestDecorators.test_getsource_unwrap @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_inspect.test_inspect.TestComplexDecorator.test_parens_in_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_inspect.test_inspect.TestDecorators.test_decorator_with_lambda @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_inspect.test_inspect.TestDecorators.test_getsource_unwrap @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestDecorators.test_replacing_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_inspect.test_inspect.TestDecorators.test_wrapped_decorator @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_inspect.test_inspect.TestDecorators.test_wrapped_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestFormatAnnotation.test_typing_replacement @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_inspect.test_inspect.TestGetAsyncGenState.test_easy_debugging @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_inspect.test_inspect.TestGetAsyncGenState.test_getasyncgenlocals @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_inspect.test_inspect.TestGetAsyncGenState.test_getasyncgenlocals_empty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_inspect.test_inspect.TestGetAsyncGenState.test_getasyncgenlocals_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_inspect.test_inspect.TestGetAsyncGenState.test_easy_debugging @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_inspect.test_inspect.TestGetAsyncGenState.test_getasyncgenlocals @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_inspect.test_inspect.TestGetAsyncGenState.test_getasyncgenlocals_empty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_inspect.test_inspect.TestGetAsyncGenState.test_getasyncgenlocals_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_inspect.test_inspect.TestGetClosureVars.test_builtins_as_dict @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestGetClosureVars.test_builtins_as_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_inspect.test_inspect.TestGetClosureVars.test_builtins_fallback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_io.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_io.txt index 1963683e66..b3838cf62e 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_io.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_io.txt @@ -195,9 +195,8 @@ test.test_io.CMiscIOTest.test_attributes @ darwin-arm64,linux-aarch64,linux-aarc test.test_io.CMiscIOTest.test_check_encoding_warning @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_io.CMiscIOTest.test_create_fail @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_io.CMiscIOTest.test_create_writes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -# GR-72206 -!test.test_io.CMiscIOTest.test_daemon_threads_shutdown_stderr_deadlock -!test.test_io.CMiscIOTest.test_daemon_threads_shutdown_stdout_deadlock +test.test_io.CMiscIOTest.test_daemon_threads_shutdown_stderr_deadlock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_io.CMiscIOTest.test_daemon_threads_shutdown_stdout_deadlock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_io.CMiscIOTest.test_io_after_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_io.CMiscIOTest.test_nonblock_pipe_write_bigbuf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_io.CMiscIOTest.test_nonblock_pipe_write_smallbuf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_isinstance.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_isinstance.txt index f3e986748e..b09c797281 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_isinstance.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_isinstance.txt @@ -9,12 +9,12 @@ test.test_isinstance.TestIsInstanceIsSubclass.test_infinite_recursion_via_bases_ test.test_isinstance.TestIsInstanceIsSubclass.test_infinitely_many_bases @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsInstanceIsSubclass.test_isinstance_abstract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsInstanceIsSubclass.test_isinstance_normal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_isinstance.TestIsInstanceIsSubclass.test_isinstance_recursion_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,win32-AMD64,win32-AMD64-github +test.test_isinstance.TestIsInstanceIsSubclass.test_isinstance_recursion_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsInstanceIsSubclass.test_isinstance_with_or_union @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsInstanceIsSubclass.test_issubclass_refcount_handling @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsInstanceIsSubclass.test_subclass_abstract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsInstanceIsSubclass.test_subclass_normal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_isinstance.TestIsInstanceIsSubclass.test_subclass_recursion_limit @ darwin-arm64,linux-aarch64,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_isinstance.TestIsInstanceIsSubclass.test_subclass_recursion_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsInstanceIsSubclass.test_subclass_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsInstanceIsSubclass.test_subclass_with_union @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_isinstance.TestIsSubclassExceptions.test_dont_mask_non_attribute_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_json.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_json.txt index d3d178b968..45e6faae1b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_json.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_json.txt @@ -115,7 +115,7 @@ test.test_json.test_recursion.TestPyRecursion.test_highly_nested_objects_encodin test.test_json.test_recursion.TestPyRecursion.test_listrecursion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_json.test_scanstring.TestCScanstring.test_bad_escapes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_json.test_scanstring.TestCScanstring.test_overflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_json.test_scanstring.TestCScanstring.test_scanstring @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_json.test_scanstring.TestCScanstring.test_scanstring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_json.test_scanstring.TestPyScanstring.test_bad_escapes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_json.test_scanstring.TestPyScanstring.test_overflow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_json.test_scanstring.TestPyScanstring.test_scanstring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_logging.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_logging.txt index a0d66f7212..731801d696 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_logging.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_logging.txt @@ -15,7 +15,7 @@ test.test_logging.BasicConfigTest.test_incompatible @ darwin-arm64,linux-aarch64 test.test_logging.BasicConfigTest.test_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_logging.BasicConfigTest.test_level @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_logging.BasicConfigTest.test_log @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_logging.BasicConfigTest.test_log_taskName @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_logging.BasicConfigTest.test_log_taskName @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_logging.BasicConfigTest.test_no_kwargs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_logging.BasicConfigTest.test_stream @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_logging.BasicConfigTest.test_strformatstyle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -134,8 +134,8 @@ test.test_logging.LogRecordTest.test_dict_arg @ darwin-arm64,linux-aarch64,linux test.test_logging.LogRecordTest.test_multiprocessing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_logging.LogRecordTest.test_optional @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_logging.LogRecordTest.test_str_rep @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_logging.LogRecordTest.test_taskName_with_asyncio_imported @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_logging.LogRecordTest.test_taskName_without_asyncio_imported @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_logging.LogRecordTest.test_taskName_with_asyncio_imported @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_logging.LogRecordTest.test_taskName_without_asyncio_imported @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_logging.LoggerAdapterTest.test_critical @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_logging.LoggerAdapterTest.test_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_logging.LoggerAdapterTest.test_exception_excinfo @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_math.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_math.txt index 2e01fd1c36..4c1f1f2e1e 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_math.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_math.txt @@ -66,7 +66,7 @@ test.test_math.MathTests.test_mtestfile @ darwin-arm64,linux-aarch64,linux-aarch test.test_math.MathTests.test_nan_constant @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_math.MathTests.test_nextafter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_math.MathTests.test_prod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_math.MathTests.test_sumprod_stress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_math.MathTests.test_sumprod_stress @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_math.MathTests.test_testfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_math.MathTests.test_trunc @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_math.MathTests.test_ulp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_metaclass.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_metaclass.txt index f6833fde29..f3131c3762 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_metaclass.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_metaclass.txt @@ -1 +1 @@ -DocTestCase.test.test_metaclass.__test__.doctests @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +DocTestCase.test.test_metaclass.__test__.doctests @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_mimetypes.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_mimetypes.txt index 0cbd15d4a6..92741c786b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_mimetypes.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_mimetypes.txt @@ -19,4 +19,5 @@ test.test_mimetypes.MimetypesCliTestCase.test_guess_type @ darwin-arm64,linux-aa test.test_mimetypes.MimetypesCliTestCase.test_help_option @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_mimetypes.MimetypesCliTestCase.test_invalid_option @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_mimetypes.MiscTestCase.test__all__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_mimetypes.Win32MimeTypesTestCase.test_registry_accelerator @ win32-AMD64 test.test_mimetypes.Win32MimeTypesTestCase.test_registry_parsing @ win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_mmap.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_mmap.txt index 4893b7e61c..a7065a7d9d 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_mmap.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_mmap.txt @@ -9,7 +9,7 @@ test.test_mmap.MmapTests.test_double_close @ darwin-arm64,linux-aarch64,linux-aa test.test_mmap.MmapTests.test_empty_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_mmap.MmapTests.test_entire_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_mmap.MmapTests.test_extended_set_del_slice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_mmap.MmapTests.test_find_does_not_access_beyond_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_mmap.MmapTests.test_find_does_not_access_beyond_buffer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_mmap.MmapTests.test_find_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_mmap.MmapTests.test_flush_return_value @ darwin-arm64,win32-AMD64,win32-AMD64-github test.test_mmap.MmapTests.test_length_0_large_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_modulefinder.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_modulefinder.txt deleted file mode 100644 index 98d9d06879..0000000000 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_modulefinder.txt +++ /dev/null @@ -1,4 +0,0 @@ -# The following two try to read bytecode and fail randomly as our co_code is changing -!graalpython.lib-python.3.test.test_modulefinder.ModuleFinderTest.test_bytecode -!graalpython.lib-python.3.test.test_modulefinder.ModuleFinderTest.test_relative_imports_4 -test.test_modulefinder.ModuleFinderTest.test_relative_imports_4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_fork.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_fork.txt deleted file mode 100644 index c62077f803..0000000000 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_fork.txt +++ /dev/null @@ -1,20 +0,0 @@ -test.test_multiprocessing_fork.test_misc.ChallengeResponseTest.test_challengeresponse @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.MiscTestCase.test__all__ @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.OtherTest.test_answer_challenge_auth_failure @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.OtherTest.test_deliver_challenge_auth_failure @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.SemLockTests.test_semlock_subclass @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestInvalidFamily.test_invalid_family @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestInvalidHandle.test_invalid_handles @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestPoolNotLeakOnFailure.test_release_unused_processes @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestResourceTracker.test_resource_tracker @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestResourceTracker.test_resource_tracker_sigint @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestResourceTracker.test_resource_tracker_sigkill @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestResourceTracker.test_resource_tracker_sigterm @ linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestResourceTracker.test_too_long_name_resource @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestSimpleQueue.test_close @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestSimpleQueue.test_empty_exceptions @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestStartMethod.test_get_all @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestStdinBadfiledescriptor.test_flushing @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestWait.test_neg_timeout @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc.TestWait.test_wait_timeout @ linux-aarch64-github,linux-x86_64-github -test.test_multiprocessing_fork.test_misc._TestImportStar.test_import @ linux-aarch64-github,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_spawn.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_spawn.txt index 3489d66460..0348aa9f81 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_spawn.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_multiprocessing_spawn.txt @@ -1,3 +1,60 @@ +test.test_multiprocessing_spawn.test_manager.WithManagerTestBarrier.test_abort @ darwin-arm64 +test.test_multiprocessing_spawn.test_manager.WithManagerTestBarrier.test_abort_and_reset @ darwin-arm64 +test.test_multiprocessing_spawn.test_manager.WithManagerTestBarrier.test_action @ darwin-arm64 +test.test_multiprocessing_spawn.test_manager.WithManagerTestBarrier.test_barrier @ darwin-arm64 +test.test_multiprocessing_spawn.test_manager.WithManagerTestBarrier.test_default_timeout @ darwin-arm64 +test.test_multiprocessing_spawn.test_manager.WithManagerTestBarrier.test_single_thread @ darwin-arm64 +test.test_multiprocessing_spawn.test_manager.WithManagerTestBarrier.test_wait_return @ darwin-arm64 +test.test_multiprocessing_spawn.test_manager.WithManagerTestCondition.test_notify @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestCondition.test_notify_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestCondition.test_notify_n @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestCondition.test_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestContainers.test_dict @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestContainers.test_dict_proxy_nested @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestContainers.test_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestContainers.test_list_iter @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestContainers.test_list_proxy_in_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestContainers.test_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestContainers.test_nested_queue @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestEvent.test_event @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestEvent.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestLock.test_lock_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestLock.test_rlock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestManagerRestart.test_rapid_restart @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestMyManager.test_mymanager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestMyManager.test_mymanager_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestMyManager.test_mymanager_context_prestarted @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_apply @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_async @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_async_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_empty_iterable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_imap @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_imap_unordered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_make_pool @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_map @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_map_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_map_async_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_map_chunksize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_map_no_failfast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_map_unplicklable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_starmap @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_starmap_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_terminate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestPool.test_wrapped_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_closed_queue_empty_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_closed_queue_put_get_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_fork @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_no_import_lock_contention @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_put @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_qsize @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestQueue.test_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestRemoteManager.test_remote @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestSemaphore.test_bounded_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_manager.WithManagerTestSemaphore.test_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.ChallengeResponseTest.test_challengeresponse @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.MiscTestCase.test__all__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.MiscTestCase.test_spawn_sys_executable_none_allows_import @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -5,7 +62,7 @@ test.test_multiprocessing_spawn.test_misc.OtherTest.test_answer_challenge_auth_f test.test_multiprocessing_spawn.test_misc.OtherTest.test_deliver_challenge_auth_failure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.SemLockTests.test_semlock_subclass @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestForkAwareThreadLock.test_lock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_multiprocessing_spawn.test_misc.TestIgnoreEINTR.test_ignore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestIgnoreEINTR.test_ignore @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestIgnoreEINTR.test_ignore_listener @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestInternalDecorators.test_only_run_in_spawn_testsuite @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestInvalidFamily.test_invalid_family @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -19,20 +76,20 @@ test.test_multiprocessing_spawn.test_misc.TestResourceTracker.test_resource_trac !test.test_multiprocessing_spawn.test_misc.TestResourceTracker.test_resource_tracker_sigint !test.test_multiprocessing_spawn.test_misc.TestResourceTracker.test_resource_tracker_sigkill !test.test_multiprocessing_spawn.test_misc.TestResourceTracker.test_resource_tracker_sigterm -test.test_multiprocessing_spawn.test_misc.TestResourceTracker.test_too_long_name_resource @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestResourceTracker.test_too_long_name_resource @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSimpleQueue.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_multiprocessing_spawn.test_misc.TestSimpleQueue.test_empty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestSimpleQueue.test_empty @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSimpleQueue.test_empty_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestStartMethod.test_get_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestStartMethod.test_nested_startmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestStdinBadfiledescriptor.test_flushing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestStdinBadfiledescriptor.test_pool_in_process @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_multiprocessing_spawn.test_misc.TestStdinBadfiledescriptor.test_queue_in_process @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestStdinBadfiledescriptor.test_queue_in_process @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_array @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_barrier @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_barrier @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_bounded_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_condition @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_dict @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_dict @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_event @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_joinable_queue @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -42,14 +99,183 @@ test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_queue @ darw test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_rlock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestSyncManagerTypes.test_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_multiprocessing_spawn.test_misc.TestTimeouts.test_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestTimeouts.test_timeout @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestWait.test_neg_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_multiprocessing_spawn.test_misc.TestWait.test_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestWait.test_wait @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestWait.test_wait_slow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_multiprocessing_spawn.test_misc.TestWait.test_wait_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_misc.TestWait.test_wait_socket @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestWait.test_wait_socket_slow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc.TestWait.test_wait_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_multiprocessing_spawn.test_misc._TestImportStar.test_import @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestArray.test_array @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestArray.test_array_from_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestArray.test_getobj_getlock_obj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestArray.test_rawarray @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestBarrier.test_abort @ darwin-arm64 +test.test_multiprocessing_spawn.test_processes.WithProcessesTestBarrier.test_abort_and_reset @ darwin-arm64 +test.test_multiprocessing_spawn.test_processes.WithProcessesTestBarrier.test_action @ darwin-arm64 +test.test_multiprocessing_spawn.test_processes.WithProcessesTestBarrier.test_barrier_10 @ darwin-arm64 +test.test_multiprocessing_spawn.test_processes.WithProcessesTestBarrier.test_reset @ darwin-arm64 +test.test_multiprocessing_spawn.test_processes.WithProcessesTestBarrier.test_single_thread @ darwin-arm64 +test.test_multiprocessing_spawn.test_processes.WithProcessesTestBarrier.test_timeout @ darwin-arm64 +test.test_multiprocessing_spawn.test_processes.WithProcessesTestBarrier.test_wait_return @ darwin-arm64 +test.test_multiprocessing_spawn.test_processes.WithProcessesTestCondition.test_notify @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestCondition.test_notify_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestCondition.test_notify_n @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestCondition.test_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestConnection.test_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestConnection.test_context @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestConnection.test_duplex_false @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestConnection.test_sendbytes @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestConnection.test_spawn_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestEvent.test_event @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestEvent.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestListener.test_abstract_socket @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestListener.test_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestListener.test_multiple_bind @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestListenerClient.test_issue14725 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestListenerClient.test_issue16955 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestListenerClient.test_listener_client @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestLock.test_lock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestLock.test_lock_context @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestLock.test_repr_lock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestLock.test_repr_rlock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestLock.test_rlock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestLogging.test_enable_logging @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestLogging.test_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestLogging.test_level @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestManagerRestart.test_rapid_restart @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoll.test_boundaries @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoll.test_dont_merge @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoll.test_empty_string @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoll.test_strings @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPollEintr.test_poll_eintr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_apply @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_async @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_async_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_empty_iterable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github !test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_enter +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_imap @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_imap_handle_iterable_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_imap_unordered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_imap_unordered_handle_iterable_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_make_pool @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_map @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_map_async @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_map_async_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_map_chunksize @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_map_handle_iterable_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_map_no_failfast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_map_unplicklable @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_starmap @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_starmap_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_terminate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPool.test_wrapped_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoolWorkerErrors.test_async_error_callback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoolWorkerErrors.test_unpickleable_result @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoolWorkerLifetime.test_pool_maxtasksperchild_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoolWorkerLifetime.test_pool_worker_lifetime @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoolWorkerLifetime.test_pool_worker_lifetime_early_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestPoolWorkerLifetime.test_worker_finalization_via_atexit_handler_of_multiprocessing @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_active_children @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_args_argument @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_child_fd_inflation @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_cpu_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_current @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_daemon_argument @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_error_on_stdio_flush_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_error_on_stdio_flush_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_kill @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_parent_process @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_parent_process_attributes @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_process @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_recursion @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_sentinel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_set_executable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_terminate @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestProcess.test_wait_for_threads @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_closed_queue_empty_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_closed_queue_put_get_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_fork @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_no_import_lock_contention @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_put @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_qsize @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_queue_feeder_donot_stop_onexc @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_queue_feeder_on_queue_feeder_error @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestQueue.test_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestSemaphore.test_bounded_semaphore @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestSemaphore.test_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestSemaphore.test_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestSpawnedSysPath.test_child_sys_path @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestSubclassingProcess.test_stderr_flush @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_processes.WithProcessesTestSubclassingProcess.test_subclassing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_abort @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_action @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_barrier @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_default_timeout @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_reset @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_single_thread @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_thousand @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_timeout @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestBarrier.test_wait_return @ darwin-arm64 +test.test_multiprocessing_spawn.test_threads.WithThreadsTestCondition.test_notify @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestCondition.test_notify_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestCondition.test_notify_n @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestCondition.test_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestConnection.test_connection @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestConnection.test_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestConnection.test_duplex_false @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestConnection.test_spawn_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestEvent.test_event @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestEvent.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestListenerClient.test_issue14725 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestListenerClient.test_issue16955 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestListenerClient.test_listener_client @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestLock.test_lock_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestLock.test_rlock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestManagerRestart.test_rapid_restart @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPoll.test_boundaries @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPoll.test_dont_merge @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPoll.test_empty_string @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPoll.test_strings @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_apply @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_async_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_empty_iterable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_enter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_imap @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_imap_unordered @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_make_pool @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_map @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_map_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_map_async_callbacks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_map_chunksize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_map_no_failfast @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_starmap @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_starmap_async @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github # transiently fails !test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_terminate +test.test_multiprocessing_spawn.test_threads.WithThreadsTestPool.test_traceback @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestProcess.test_active_children @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestProcess.test_args_argument @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestProcess.test_cpu_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestProcess.test_error_on_stdio_flush_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestProcess.test_error_on_stdio_flush_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestProcess.test_process @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestProcess.test_recursion @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_closed_queue_empty_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_closed_queue_put_get_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_fork @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_get @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_no_import_lock_contention @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_put @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_qsize @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestQueue.test_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestSemaphore.test_bounded_semaphore @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_multiprocessing_spawn.test_threads.WithThreadsTestSemaphore.test_semaphore @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_os.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_os.txt index 62cfb38d77..bafcb8af62 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_os.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_os.txt @@ -21,7 +21,7 @@ test.test_os.BytesWalkTests.test_walk_prune @ darwin-arm64,linux-aarch64,linux-a test.test_os.BytesWalkTests.test_walk_symlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_os.BytesWalkTests.test_walk_topdown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_os.CPUCountTests.test_cpu_count @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_os.ChownFileTests.test_chown_gid @ linux-aarch64-github,linux-x86_64-github +test.test_os.ChownFileTests.test_chown_gid @ darwin-arm64,linux-aarch64-github,linux-x86_64-github test.test_os.ChownFileTests.test_chown_uid_gid_arguments_must_be_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_os.ChownFileTests.test_chown_without_permission @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_os.DevNullTests.test_devnull @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_patched_pip.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_patched_pip.txt deleted file mode 100644 index 594153152c..0000000000 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_patched_pip.txt +++ /dev/null @@ -1,22 +0,0 @@ -test.test_patched_pip.PipPatchingTest.test_broken_patches_path @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_different_patch_wheel_sdist1 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_different_patch_wheel_sdist2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_name_with_dashes @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_name_with_underscores @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_patches_file_url @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_patches_http_url @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_patches_repo_version_resolution @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_patches_repo_version_resolution_dev @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_rule_matching1 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_rule_matching2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_sdist_patched_version @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_sdist_unpatched_version @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_version_range_inside @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_version_range_multiple @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_version_range_outside @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_version_selection_default @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_version_selection_explicit_demoted @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_version_selection_explicit_promoted @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_version_selection_no_patch @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_wheel_patched_version @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_patched_pip.PipPatchingTest.test_wheel_unpatched_version @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_pdb.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_pdb.txt index 49a0b9f775..3918ca8675 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_pdb.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_pdb.txt @@ -3,7 +3,7 @@ DocTestCase.test.test_pdb.test_list_commands @ darwin-arm64,linux-aarch64,linux- DocTestCase.test.test_pdb.test_next_until_return_at_return_event @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_alias_command @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_ambiguous_statements @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71866 +# Arbitrary jumps in pdb not supported in Bytecode DSL !DocTestCase.test.test_pdb.test_pdb_basic_commands @ darwin-arm64,linux-x86_64,win32-AMD64 DocTestCase.test.test_pdb.test_pdb_breakpoint_commands @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_breakpoint_on_annotated_function_def @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -17,9 +17,12 @@ DocTestCase.test.test_pdb.test_pdb_issue_43318 @ darwin-arm64,linux-aarch64,linu DocTestCase.test.test_pdb.test_pdb_issue_gh_101673 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_issue_gh_103225 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_issue_gh_65052 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71866 +# Arbitrary jumps in pdb not supported in Bytecode DSL !DocTestCase.test.test_pdb.test_pdb_issue_gh_91742 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +DocTestCase.test.test_pdb.test_pdb_next_command_for_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_pp_repr_exc @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +DocTestCase.test.test_pdb.test_pdb_return_command_for_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +DocTestCase.test.test_pdb.test_pdb_return_command_for_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_return_to_different_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_run_with_code_object @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github DocTestCase.test.test_pdb.test_pdb_skip_modules @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -40,18 +43,14 @@ test.test_pdb.PdbTestCase.test_gh_93696_frozen_list @ darwin-arm64,linux-aarch64 test.test_pdb.PdbTestCase.test_gh_94215_crash @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_pdb.PdbTestCase.test_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_pdb.PdbTestCase.test_invalid_cmd_line_options @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71918 -!test.test_pdb.PdbTestCase.test_issue13120 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_pdb.PdbTestCase.test_issue13120 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_pdb.PdbTestCase.test_issue13120 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_pdb.PdbTestCase.test_issue13183 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_pdb.PdbTestCase.test_issue16180 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_pdb.PdbTestCase.test_issue26053 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_pdb.PdbTestCase.test_issue34266 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_pdb.PdbTestCase.test_issue36250 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -# GR-71918 -!test.test_pdb.PdbTestCase.test_issue36250 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_pdb.PdbTestCase.test_issue42383 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_pdb.PdbTestCase.test_issue42384 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_pdb.PdbTestCase.test_issue36250 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_pdb.PdbTestCase.test_issue42383 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_pdb.PdbTestCase.test_issue42384 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_pdb.PdbTestCase.test_issue42384_symlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_pdb.PdbTestCase.test_issue46434 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_pdb.PdbTestCase.test_issue7964 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_platform.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_platform.txt index b67abbbf3e..4f79e0c868 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_platform.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_platform.txt @@ -1,5 +1,5 @@ test.test_platform.PlatformTest.test_architecture @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_platform.PlatformTest.test_architecture_via_symlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_platform.PlatformTest.test_architecture_via_symlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_platform.PlatformTest.test_freedesktop_os_release @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_platform.PlatformTest.test_java_ver @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_platform.PlatformTest.test_libc_ver @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_profile.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_profile.txt index 4664aef0c0..8c8bd0681a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_profile.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_profile.txt @@ -1,7 +1,6 @@ -# GR-71916 -!test.test_profile.ProfileTest.test_calling_conventions @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64,win32-AMD64-github -!test.test_profile.ProfileTest.test_cprofile @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64,win32-AMD64-github +test.test_profile.ProfileTest.test_calling_conventions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_profile.ProfileTest.test_cprofile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_profile.ProfileTest.test_output_file_when_changing_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_profile.ProfileTest.test_run @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_profile.ProfileTest.test_run_profile_as_module @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64,win32-AMD64-github +test.test_profile.ProfileTest.test_run_profile_as_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_profile.ProfileTest.test_runctx @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_property.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_property.txt index 42b87ed1fd..d24c11173c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_property.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_property.txt @@ -3,6 +3,8 @@ test.test_property.PropertySubclassTests.test_property_new_getter_new_docstring test.test_property.PropertySubclassTests.test_property_setter_copies_getter_docstring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_property.PropertySubclassTests.test_property_with_slots_docstring_silently_dropped @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_property.PropertySubclassTests.test_slots_docstring_copy_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_property.PropertyTests.test_class_property @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_property.PropertyTests.test_class_property_override @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_property.PropertyTests.test_property___isabstractmethod__descriptor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_property.PropertyTests.test_property_builtin_doc_writable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_property.PropertyTests.test_property_decorator_baseclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_queue.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_queue.txt index 102792430e..9fb7b59d71 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_queue.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_queue.txt @@ -45,7 +45,7 @@ test.test_queue.PyQueueTest.test_queue_join @ darwin-arm64,linux-aarch64,linux-a test.test_queue.PyQueueTest.test_queue_task_done @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_queue.PyQueueTest.test_shrinking_queue @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_queue.PySimpleQueueTest.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_queue.PySimpleQueueTest.test_many_threads @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_queue.PySimpleQueueTest.test_many_threads @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_queue.PySimpleQueueTest.test_many_threads_nonblock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_queue.PySimpleQueueTest.test_many_threads_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_queue.PySimpleQueueTest.test_negative_timeout_raises_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_robotparser.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_robotparser.txt index 5ee3aa92a4..43c3d757ca 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_robotparser.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_robotparser.txt @@ -35,9 +35,9 @@ test.test_robotparser.InvalidCrawlDelayTest.test_site_maps @ darwin-arm64,linux- test.test_robotparser.InvalidRequestRateTest.test_bad_urls @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_robotparser.InvalidRequestRateTest.test_good_urls @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_robotparser.InvalidRequestRateTest.test_site_maps @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_robotparser.NetworkTestCase.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_robotparser.NetworkTestCase.test_can_fetch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_robotparser.NetworkTestCase.test_read_404 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_robotparser.NetworkTestCase.test_basic @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_robotparser.NetworkTestCase.test_can_fetch @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_robotparser.NetworkTestCase.test_read_404 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_robotparser.PasswordProtectedSiteTestCase.testPasswordProtectedSite @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_robotparser.RejectAllRobotsTest.test_bad_urls @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_robotparser.RejectAllRobotsTest.test_good_urls @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_runpy.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_runpy.txt index 9570bc0f2c..6b0e30d95f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_runpy.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_runpy.txt @@ -24,7 +24,7 @@ test.test_runpy.RunPathTestCase.test_directory @ darwin-arm64,linux-aarch64,linu test.test_runpy.RunPathTestCase.test_directory_compiled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_runpy.RunPathTestCase.test_directory_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_runpy.RunPathTestCase.test_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_runpy.RunPathTestCase.test_main_recursion_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_runpy.RunPathTestCase.test_main_recursion_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_runpy.RunPathTestCase.test_script_compiled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_runpy.RunPathTestCase.test_zipfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_runpy.RunPathTestCase.test_zipfile_compiled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_scope.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_scope.txt index d643850a96..e89dba58d2 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_scope.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_scope.txt @@ -1,37 +1,37 @@ -test.test_scope.ScopeTests.testBoundAndFree @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testCellIsArgAndEscapes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testCellIsKwonlyArg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testCellIsLocalAndEscapes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testClassAndGlobal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testClassNamespaceOverridesClosure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testComplexDefinitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testEvalExecFreeVars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testEvalFreeVars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testExtraNesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testFreeVarInMethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testFreeingCell @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testGlobalInParallelNestedFunctions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testLambdas @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testListCompLocalVars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testLocalsClass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testLocalsFunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testMixedFreevarsAndCellvars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNearestEnclosingScope @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNestedNonLocal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNestingGlobalNoFree @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNestingPlusFreeRefToGlobal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNestingThroughClass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNonLocalClass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNonLocalFunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNonLocalGenerator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testNonLocalMethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testRecursion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testScopeOfGlobalStmt @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testSimpleAndRebinding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testSimpleNesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testTopIsNotSignificant @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testUnboundLocal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testUnboundLocal_AfterDel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testUnboundLocal_AugAssign @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.testUnoptimizedNamespaces @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_scope.ScopeTests.test_multiple_nesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_scope.ScopeTests.testBoundAndFree @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testCellIsArgAndEscapes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testCellIsKwonlyArg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testCellIsLocalAndEscapes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testClassAndGlobal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testClassNamespaceOverridesClosure @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testComplexDefinitions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testEvalExecFreeVars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testEvalFreeVars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testExtraNesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testFreeVarInMethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testFreeingCell @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testGlobalInParallelNestedFunctions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testLambdas @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testListCompLocalVars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testLocalsClass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testLocalsFunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testMixedFreevarsAndCellvars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNearestEnclosingScope @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNestedNonLocal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNestingGlobalNoFree @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNestingPlusFreeRefToGlobal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNestingThroughClass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNonLocalClass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNonLocalFunction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNonLocalGenerator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testNonLocalMethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testRecursion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testScopeOfGlobalStmt @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testSimpleAndRebinding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testSimpleNesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testTopIsNotSignificant @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testUnboundLocal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testUnboundLocal_AfterDel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testUnboundLocal_AugAssign @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.testUnoptimizedNamespaces @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_scope.ScopeTests.test_multiple_nesting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_signal.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_signal.txt index d87d3c60b5..ea174f7409 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_signal.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_signal.txt @@ -1,11 +1,16 @@ test.test_signal.GenericTests.test_enums @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_signal.GenericTests.test_functions_module_attr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_signal.ItimerTest.test_itimer_exc @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_signal.ItimerTest.test_itimer_prof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +# Transient: delayed SIGVTALRM may be delivered to the next test on GraalPy +!test.test_signal.ItimerTest.test_itimer_virtual test.test_signal.ItimerTest.test_setitimer_tiny @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_signal.PosixTests.test_getsignal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_signal.PosixTests.test_no_repr_is_called_on_signal_handler @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_signal.PosixTests.test_setting_signal_handler_to_none_raises_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_signal.PosixTests.test_valid_signals @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_signal.RaiseSignalTest.test_handler @ linux-x86_64-github +test.test_signal.RaiseSignalTest.test_handler @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +!test.test_signal.RaiseSignalTest.test_sigint @ linux-x86_64-github test.test_signal.SiginterruptTest.test_siginterrupt_off @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github # Timeout !test.test_signal.StressTest.test_stress_modifying_handlers diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_site.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_site.txt index 1879a1593b..369ba5dddf 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_site.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_site.txt @@ -16,7 +16,6 @@ test.test_site.HelperFunctionsTests.test_no_home_directory @ darwin-arm64,linux- test.test_site.HelperFunctionsTests.test_s_option @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_site.HelperFunctionsTests.test_trace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_site.ImportSideEffectTests.test_abs_paths_cached_None @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_site.ImportSideEffectTests.test_license_exists_at_url @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_site.ImportSideEffectTests.test_no_duplicate_paths @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_site.ImportSideEffectTests.test_setting_copyright @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_site.ImportSideEffectTests.test_setting_help @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -25,3 +24,5 @@ test.test_site.ImportSideEffectTests.test_sitecustomize_executed @ darwin-arm64, test.test_site.StartupImportTests.test_startup_interactivehook @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_site.StartupImportTests.test_startup_interactivehook_isolated @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_site.StartupImportTests.test_startup_interactivehook_isolated_explicit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_site._pthFileTests.test_underpth_dll_file @ win32-AMD64 +test.test_site._pthFileTests.test_underpth_file @ win32-AMD64 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sqlite3.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sqlite3.txt index be938dbfc7..19e52549bd 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sqlite3.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sqlite3.txt @@ -277,6 +277,7 @@ test.test_sqlite3.test_regression.RegressionTests.test_convert_timestamp_microse test.test_sqlite3.test_regression.RegressionTests.test_cursor_constructor_call_check @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_regression.RegressionTests.test_del_isolation_level_segfault @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_regression.RegressionTests.test_empty_statement @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sqlite3.test_regression.RegressionTests.test_error_msg_decode_error @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 test.test_sqlite3.test_regression.RegressionTests.test_executescript_step_through_select @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_regression.RegressionTests.test_invalid_isolation_level_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_regression.RegressionTests.test_large_sql @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -339,7 +340,7 @@ test.test_sqlite3.test_transactions.TransactionTests.test_locking @ darwin-arm64 test.test_sqlite3.test_transactions.TransactionTests.test_multiple_cursors_and_iternext @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_sqlite3.test_transactions.TransactionTests.test_raise_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_sqlite3.test_transactions.TransactionTests.test_replace_starts_transaction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_sqlite3.test_transactions.TransactionTests.test_rollback_cursor_consistency @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_sqlite3.test_transactions.TransactionTests.test_rollback_cursor_consistency @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_transactions.TransactionTests.test_toggle_auto_commit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_sqlite3.test_transactions.TransactionTests.test_update_starts_transaction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_sqlite3.test_transactions.TransactionalDDL.test_ddl_does_not_autostart_transaction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -438,12 +439,14 @@ test.test_sqlite3.test_userfunctions.FunctionTests.test_func_return_nan @ darwin test.test_sqlite3.test_userfunctions.FunctionTests.test_func_return_null @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.FunctionTests.test_func_return_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.FunctionTests.test_func_return_text_with_null_char @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sqlite3.test_userfunctions.FunctionTests.test_func_return_text_with_surrogates @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 test.test_sqlite3.test_userfunctions.FunctionTests.test_func_return_too_large_int @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.FunctionTests.test_func_return_unicode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.FunctionTests.test_func_too_many_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.FunctionTests.test_function_destructor_via_gc @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.FunctionTests.test_nan_float @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.FunctionTests.test_non_contiguous_blob @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sqlite3.test_userfunctions.FunctionTests.test_param_surrogates @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 test.test_sqlite3.test_userfunctions.FunctionTests.test_return_non_contiguous_blob @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.FunctionTests.test_too_large_int @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sqlite3.test_userfunctions.WindowFunctionTests.test_win_clear_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ssl.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ssl.txt index 9b94186a5d..1c0ede2faf 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ssl.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ssl.txt @@ -77,7 +77,7 @@ test.test_ssl.TestEnumerations.test_verifymode @ darwin-arm64,linux-aarch64,linu test.test_ssl.TestPreHandshakeClose.test_https_client_non_tls_response_ignored @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.TestPreHandshakeClose.test_preauth_data_to_tls_client @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.TestPreHandshakeClose.test_preauth_data_to_tls_server @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_ssl.ThreadedTests.test_PROTOCOL_TLS @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_ssl.ThreadedTests.test_PROTOCOL_TLS @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.ThreadedTests.test_asyncore_server @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.ThreadedTests.test_check_hostname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.ThreadedTests.test_check_hostname_idn @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -95,7 +95,7 @@ test.test_ssl.ThreadedTests.test_legacy_server_connect @ darwin-arm64,linux-aarc test.test_ssl.ThreadedTests.test_no_legacy_server_connect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.ThreadedTests.test_nonblocking_send @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.ThreadedTests.test_npn_protocols @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_ssl.ThreadedTests.test_protocol_tlsv1_2 @ darwin-arm64,linux-aarch64,linux-x86_64 +test.test_ssl.ThreadedTests.test_protocol_tlsv1_2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.ThreadedTests.test_read_write_after_close_raises_valuerror @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.ThreadedTests.test_recv_into_buffer_protocol_len @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_ssl.ThreadedTests.test_recv_send @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_statistics.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_statistics.txt index 6ee5c001e7..094c859a0f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_statistics.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_statistics.txt @@ -244,7 +244,7 @@ test.test_statistics.TestNormalDistPython.test_equality @ darwin-arm64,linux-aar test.test_statistics.TestNormalDistPython.test_hashability @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_statistics.TestNormalDistPython.test_instantiation_and_attributes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_statistics.TestNormalDistPython.test_inv_cdf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_statistics.TestNormalDistPython.test_overlap @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_statistics.TestNormalDistPython.test_overlap @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_statistics.TestNormalDistPython.test_pdf @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_statistics.TestNormalDistPython.test_pickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_statistics.TestNormalDistPython.test_properties @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_strptime.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_strptime.txt index 3d9b76b04c..9f99a61b5f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_strptime.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_strptime.txt @@ -12,7 +12,9 @@ test.test_strptime.LocaleTime_Tests.test_am_pm @ darwin-arm64,linux-aarch64,linu test.test_strptime.LocaleTime_Tests.test_date_time @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_strptime.LocaleTime_Tests.test_lang @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_strptime.LocaleTime_Tests.test_month @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_strptime.LocaleTime_Tests.test_timezone @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_strptime.LocaleTime_Tests.test_timezone @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +# Transiently fails with trying to look up "gmt+1:00" instead of "gmt" for unknown reasons +!test.test_strptime.LocaleTime_Tests.test_timezone @ darwin-arm64 test.test_strptime.LocaleTime_Tests.test_weekday @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_strptime.Strptime12AMPMTests.test_twelve_noon_midnight @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_strptime.StrptimeTests.test_ValueError @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_subprocess.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_subprocess.txt index d6598dac7b..475953d465 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_subprocess.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_subprocess.txt @@ -38,6 +38,7 @@ test.test_subprocess.POSIXProcessTestCase.test_group_error @ darwin-arm64,linux- test.test_subprocess.POSIXProcessTestCase.test_invalid_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_subprocess.POSIXProcessTestCase.test_kill @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_subprocess.POSIXProcessTestCase.test_kill_dead @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_subprocess.POSIXProcessTestCase.test_process_group_0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_subprocess.POSIXProcessTestCase.test_remapping_std_fds @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_subprocess.POSIXProcessTestCase.test_select_unbuffered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_subprocess.POSIXProcessTestCase.test_send_signal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sys_setprofile.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sys_setprofile.txt index c97dd3c406..8b39dd664c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sys_setprofile.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sys_setprofile.txt @@ -4,15 +4,13 @@ test.test_sys_setprofile.ProfileHookTestCase.test_distant_exception @ darwin-arm test.test_sys_setprofile.ProfileHookTestCase.test_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileHookTestCase.test_exception_in_except_clause @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileHookTestCase.test_exception_propagation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71865 -!test.test_sys_setprofile.ProfileHookTestCase.test_generator @ darwin-arm64,linux-x86_64,win32-AMD64 +test.test_sys_setprofile.ProfileHookTestCase.test_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileHookTestCase.test_nested_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileHookTestCase.test_raise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileHookTestCase.test_raise_reraise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileHookTestCase.test_raise_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileHookTestCase.test_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71865 -!test.test_sys_setprofile.ProfileHookTestCase.test_stop_iteration @ darwin-arm64,linux-x86_64,win32-AMD64 +test.test_sys_setprofile.ProfileHookTestCase.test_stop_iteration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileSimulatorTestCase.test_basic_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileSimulatorTestCase.test_caught_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileSimulatorTestCase.test_distant_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -22,6 +20,11 @@ test.test_sys_setprofile.ProfileSimulatorTestCase.test_unbound_method_invalid_ar test.test_sys_setprofile.ProfileSimulatorTestCase.test_unbound_method_invalid_keyword_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileSimulatorTestCase.test_unbound_method_no_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.ProfileSimulatorTestCase.test_unbound_method_no_keyword_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_setprofile.TestEdgeCases.test_c_call_after_python_argument_evaluation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_setprofile.TestEdgeCases.test_method_with_c_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_setprofile.TestEdgeCases.test_nested_c_call_after_inner_return @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_setprofile.TestEdgeCases.test_nested_c_call_argument_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_setprofile.TestEdgeCases.test_nested_c_call_callback_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.TestEdgeCases.test_same_object @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.TestGetProfile.test_empty @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_setprofile.TestGetProfile.test_setget @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sys_settrace.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sys_settrace.txt index 94328785e8..9edeb6250f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sys_settrace.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sys_settrace.txt @@ -19,9 +19,7 @@ test.test_sys_settrace.SkipLineEventsTraceTestCase.test_10_ireturn @ darwin-arm6 test.test_sys_settrace.SkipLineEventsTraceTestCase.test_11_tightloop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_12_tighterloop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_14_onliner_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.SkipLineEventsTraceTestCase.test_15_loops @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -# GR-71864 -!test.test_sys_settrace.SkipLineEventsTraceTestCase.test_15_loops @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.SkipLineEventsTraceTestCase.test_15_loops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_16_blank_lines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_17_none_f_trace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_18_except_with_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -34,10 +32,9 @@ test.test_sys_settrace.SkipLineEventsTraceTestCase.test_break_to_continue2 @ dar test.test_sys_settrace.SkipLineEventsTraceTestCase.test_class_creation_with_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_class_creation_with_docstrings @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_continue_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.SkipLineEventsTraceTestCase.test_early_exit_with @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.SkipLineEventsTraceTestCase.test_early_exit_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_finally_with_conditional @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.SkipLineEventsTraceTestCase.test_flow_converges_on_same_line @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.SkipLineEventsTraceTestCase.test_flow_converges_on_same_line @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.SkipLineEventsTraceTestCase.test_flow_converges_on_same_line @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_if_break @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_if_false_in_try_except @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_if_false_in_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -50,11 +47,11 @@ test.test_sys_settrace.SkipLineEventsTraceTestCase.test_nested_loops @ darwin-ar test.test_sys_settrace.SkipLineEventsTraceTestCase.test_nested_try_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_no_tracing_of_named_except_cleanup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_notrace_lambda @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.SkipLineEventsTraceTestCase.test_return_through_finally @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.SkipLineEventsTraceTestCase.test_return_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_set_and_retrieve_func @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_set_and_retrieve_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_settrace_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.SkipLineEventsTraceTestCase.test_tracing_exception_raised_in_with @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.SkipLineEventsTraceTestCase.test_tracing_exception_raised_in_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_star_exception_caught @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_star_exception_not_caught @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -63,8 +60,7 @@ test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_star_named_ex test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_star_named_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_star_nested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_star_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_with_wrong_type @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_with_wrong_type @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_except_with_wrong_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_exception_in_else @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_in_try @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.SkipLineEventsTraceTestCase.test_try_in_try_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -77,8 +73,7 @@ test.test_sys_settrace.TestLinesAfterTraceStarted.test_02_arigo1 @ darwin-arm64, test.test_sys_settrace.TestLinesAfterTraceStarted.test_02_arigo2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_03_one_instr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_04_no_pop_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TestLinesAfterTraceStarted.test_05_no_pop_tops @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.TestLinesAfterTraceStarted.test_05_no_pop_tops @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TestLinesAfterTraceStarted.test_05_no_pop_tops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_06_call @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_07_raise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_08_settrace_and_return @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -87,8 +82,7 @@ test.test_sys_settrace.TestLinesAfterTraceStarted.test_10_ireturn @ darwin-arm64 test.test_sys_settrace.TestLinesAfterTraceStarted.test_11_tightloop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_12_tighterloop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_14_onliner_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TestLinesAfterTraceStarted.test_15_loops @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TestLinesAfterTraceStarted.test_15_loops @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestLinesAfterTraceStarted.test_15_loops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_16_blank_lines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_17_none_f_trace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_18_except_with_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -96,16 +90,14 @@ test.test_sys_settrace.TestLinesAfterTraceStarted.test_19_except_with_finally @ test.test_sys_settrace.TestLinesAfterTraceStarted.test_21_repeated_pass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_break_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_break_to_break @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TestLinesAfterTraceStarted.test_break_to_continue1 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TestLinesAfterTraceStarted.test_break_to_continue1 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.TestLinesAfterTraceStarted.test_break_to_continue2 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TestLinesAfterTraceStarted.test_break_to_continue2 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestLinesAfterTraceStarted.test_break_to_continue1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_settrace.TestLinesAfterTraceStarted.test_break_to_continue2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_class_creation_with_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_continue_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_settrace.TestLinesAfterTraceStarted.test_early_exit_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_events @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_finally_with_conditional @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TestLinesAfterTraceStarted.test_flow_converges_on_same_line @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.TestLinesAfterTraceStarted.test_flow_converges_on_same_line @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TestLinesAfterTraceStarted.test_flow_converges_on_same_line @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_if_break @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_if_false_in_try_except @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_if_false_in_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -118,11 +110,11 @@ test.test_sys_settrace.TestLinesAfterTraceStarted.test_nested_loops @ darwin-arm test.test_sys_settrace.TestLinesAfterTraceStarted.test_nested_try_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_no_tracing_of_named_except_cleanup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_notrace_lambda @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TestLinesAfterTraceStarted.test_return_through_finally @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TestLinesAfterTraceStarted.test_return_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_set_and_retrieve_func @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_set_and_retrieve_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_settrace_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TestLinesAfterTraceStarted.test_tracing_exception_raised_in_with @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestLinesAfterTraceStarted.test_tracing_exception_raised_in_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_star_exception_caught @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_star_exception_not_caught @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -131,8 +123,7 @@ test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_star_named_exc test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_star_named_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_star_nested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_star_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_with_wrong_type @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_with_wrong_type @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_except_with_wrong_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_exception_in_else @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_in_try @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestLinesAfterTraceStarted.test_try_in_try_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -143,8 +134,7 @@ test.test_sys_settrace.TestSetLocalTrace.test_02_arigo1 @ darwin-arm64,linux-aar test.test_sys_settrace.TestSetLocalTrace.test_02_arigo2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_03_one_instr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_04_no_pop_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TestSetLocalTrace.test_05_no_pop_tops @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TestSetLocalTrace.test_05_no_pop_tops @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestSetLocalTrace.test_05_no_pop_tops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_06_call @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_07_raise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_08_settrace_and_return @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -153,8 +143,7 @@ test.test_sys_settrace.TestSetLocalTrace.test_10_ireturn @ darwin-arm64,linux-aa test.test_sys_settrace.TestSetLocalTrace.test_11_tightloop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_12_tighterloop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_14_onliner_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TestSetLocalTrace.test_15_loops @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.TestSetLocalTrace.test_15_loops @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TestSetLocalTrace.test_15_loops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_16_blank_lines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_17_none_f_trace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_18_except_with_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -162,15 +151,13 @@ test.test_sys_settrace.TestSetLocalTrace.test_19_except_with_finally @ darwin-ar test.test_sys_settrace.TestSetLocalTrace.test_21_repeated_pass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_break_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_break_to_break @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TestSetLocalTrace.test_break_to_continue1 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.TestSetLocalTrace.test_break_to_continue1 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -!test.test_sys_settrace.TestSetLocalTrace.test_break_to_continue2 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TestSetLocalTrace.test_break_to_continue2 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestSetLocalTrace.test_break_to_continue1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_settrace.TestSetLocalTrace.test_break_to_continue2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_class_creation_with_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_continue_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_settrace.TestSetLocalTrace.test_early_exit_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_finally_with_conditional @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TestSetLocalTrace.test_flow_converges_on_same_line @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TestSetLocalTrace.test_flow_converges_on_same_line @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestSetLocalTrace.test_flow_converges_on_same_line @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_if_break @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_if_false_in_try_except @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_if_false_in_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -183,11 +170,11 @@ test.test_sys_settrace.TestSetLocalTrace.test_nested_loops @ darwin-arm64,linux- test.test_sys_settrace.TestSetLocalTrace.test_nested_try_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_no_tracing_of_named_except_cleanup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_notrace_lambda @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TestSetLocalTrace.test_return_through_finally @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TestSetLocalTrace.test_return_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_set_and_retrieve_func @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_set_and_retrieve_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_settrace_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TestSetLocalTrace.test_tracing_exception_raised_in_with @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestSetLocalTrace.test_tracing_exception_raised_in_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_try_except_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_try_except_star_exception_caught @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_try_except_star_exception_not_caught @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -196,8 +183,7 @@ test.test_sys_settrace.TestSetLocalTrace.test_try_except_star_named_exception_no test.test_sys_settrace.TestSetLocalTrace.test_try_except_star_named_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_try_except_star_nested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_try_except_star_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TestSetLocalTrace.test_try_except_with_wrong_type @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TestSetLocalTrace.test_try_except_with_wrong_type @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TestSetLocalTrace.test_try_except_with_wrong_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_try_exception_in_else @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_try_in_try @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TestSetLocalTrace.test_try_in_try_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -209,8 +195,7 @@ test.test_sys_settrace.TraceTestCase.test_02_arigo1 @ darwin-arm64,linux-aarch64 test.test_sys_settrace.TraceTestCase.test_02_arigo2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_03_one_instr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_04_no_pop_blocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TraceTestCase.test_05_no_pop_tops @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TraceTestCase.test_05_no_pop_tops @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TraceTestCase.test_05_no_pop_tops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_06_call @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_07_raise @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_08_settrace_and_return @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -219,8 +204,7 @@ test.test_sys_settrace.TraceTestCase.test_10_ireturn @ darwin-arm64,linux-aarch6 test.test_sys_settrace.TraceTestCase.test_11_tightloop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_12_tighterloop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_14_onliner_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TraceTestCase.test_15_loops @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.TraceTestCase.test_15_loops @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TraceTestCase.test_15_loops @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_16_blank_lines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_17_none_f_trace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_18_except_with_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -228,15 +212,13 @@ test.test_sys_settrace.TraceTestCase.test_19_except_with_finally @ darwin-arm64, test.test_sys_settrace.TraceTestCase.test_21_repeated_pass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_break_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_break_to_break @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TraceTestCase.test_break_to_continue1 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TraceTestCase.test_break_to_continue1 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -test.test_sys_settrace.TraceTestCase.test_break_to_continue2 @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.TraceTestCase.test_break_to_continue2 @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TraceTestCase.test_break_to_continue1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_settrace.TraceTestCase.test_break_to_continue2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_class_creation_with_decorator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_continue_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_sys_settrace.TraceTestCase.test_early_exit_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_finally_with_conditional @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TraceTestCase.test_flow_converges_on_same_line @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -test.test_sys_settrace.TraceTestCase.test_flow_converges_on_same_line @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TraceTestCase.test_flow_converges_on_same_line @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_if_break @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_if_false_in_try_except @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_if_false_in_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -249,11 +231,11 @@ test.test_sys_settrace.TraceTestCase.test_nested_loops @ darwin-arm64,linux-aarc test.test_sys_settrace.TraceTestCase.test_nested_try_if @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_no_tracing_of_named_except_cleanup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_notrace_lambda @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -!test.test_sys_settrace.TraceTestCase.test_return_through_finally @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TraceTestCase.test_return_through_finally @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_set_and_retrieve_func @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_set_and_retrieve_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_settrace_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TraceTestCase.test_tracing_exception_raised_in_with @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_sys_settrace.TraceTestCase.test_tracing_exception_raised_in_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_try_except_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_try_except_star_exception_caught @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_try_except_star_exception_not_caught @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -262,8 +244,7 @@ test.test_sys_settrace.TraceTestCase.test_try_except_star_named_exception_not_ca test.test_sys_settrace.TraceTestCase.test_try_except_star_named_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_try_except_star_nested @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_try_except_star_no_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sys_settrace.TraceTestCase.test_try_except_with_wrong_type @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -!test.test_sys_settrace.TraceTestCase.test_try_except_with_wrong_type @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_sys_settrace.TraceTestCase.test_try_except_with_wrong_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_try_exception_in_else @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_try_in_try @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sys_settrace.TraceTestCase.test_try_in_try_with_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sysconfig.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sysconfig.txt index c959903ffc..1fd7080f67 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sysconfig.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_sysconfig.txt @@ -18,6 +18,6 @@ test.test_sysconfig.TestSysConfig.test_paths_depend_on_site_initialization @ dar test.test_sysconfig.TestSysConfig.test_platform_in_subprocess @ darwin-arm64 test.test_sysconfig.TestSysConfig.test_posix_venv_scheme @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sysconfig.TestSysConfig.test_srcdir_independent_of_cwd @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_sysconfig.TestSysConfig.test_symlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_sysconfig.TestSysConfig.test_symlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_sysconfig.TestSysConfig.test_user_similar @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_sysconfig.TestSysConfig.test_venv_scheme @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_tarfile.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_tarfile.txt index 2784c1c14c..1dbceb011f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_tarfile.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_tarfile.txt @@ -2,12 +2,12 @@ test.test_tarfile.AppendTest.test_empty @ darwin-arm64,linux-aarch64,linux-aarch test.test_tarfile.AppendTest.test_empty_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.AppendTest.test_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.AppendTest.test_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.AppendTest.test_incomplete @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.AppendTest.test_incomplete @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.AppendTest.test_invalid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.AppendTest.test_non_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.AppendTest.test_non_existing @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.AppendTest.test_null @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.AppendTest.test_premature_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.AppendTest.test_trailing_garbage @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.AppendTest.test_trailing_garbage @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2AppendTest.test_append_compressed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2CompressStreamWriteTest.test_compression_levels @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2CompressWriteTest.test_compression_levels @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -18,12 +18,12 @@ test.test_tarfile.Bz2CreateTest.test_create_pathlike_name @ darwin-arm64,linux-a test.test_tarfile.Bz2CreateTest.test_create_taropen @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2CreateTest.test_create_taropen_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2CreateTest.test_create_with_compresslevel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2CreateTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2CreateTest.test_eof_marker @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2CreateTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2DetectReadTest.test_detect_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2DetectReadTest.test_detect_file @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2DetectReadTest.test_detect_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2DetectReadTest.test_detect_stream_bz2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2ListTest.test_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2ListTest.test_list @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2ListTest.test_list_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_check_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_deprecation_if_no_filter_passed_to_extract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -32,12 +32,12 @@ test.test_tarfile.Bz2MiscReadTest.test_empty_name_attribute @ darwin-arm64,linux test.test_tarfile.Bz2MiscReadTest.test_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_extract_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.Bz2MiscReadTest.test_extract_hardlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2MiscReadTest.test_extract_pathlike_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2MiscReadTest.test_extract_pathlike_dir @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_extractall @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.Bz2MiscReadTest.test_extractall_pathlike_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2MiscReadTest.test_extractall_pathlike_dir @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_extractfile_attrs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_fail_comp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2MiscReadTest.test_fileobj_with_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2MiscReadTest.test_fileobj_with_offset @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_find_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_ignore_zeros @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_illegal_mode_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -49,10 +49,10 @@ test.test_tarfile.Bz2MiscReadTest.test_is_tarfile_valid @ darwin-arm64,linux-aar test.test_tarfile.Bz2MiscReadTest.test_length_zero_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_next_on_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.Bz2MiscReadTest.test_no_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2MiscReadTest.test_non_existent_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2MiscReadTest.test_non_existent_tarfile @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_null_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_parallel_iteration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2MiscReadTest.test_pathlike_bytes_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2MiscReadTest.test_pathlike_bytes_name @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2MiscReadTest.test_premature_end_of_archive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.Bz2MiscReadTest.test_v7_dirtype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -63,12 +63,12 @@ test.test_tarfile.Bz2PartialReadTest.test_partial_input_bz2 @ darwin-arm64,linux test.test_tarfile.Bz2StreamReadTest.test_compare_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamReadTest.test_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamReadTest.test_extractfile_attrs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2StreamReadTest.test_fileobj_regular_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2StreamReadTest.test_fileobj_regular_file @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamReadTest.test_ignore_zeros @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2StreamReadTest.test_is_tarfile_erroneous @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2StreamReadTest.test_is_tarfile_erroneous @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamReadTest.test_is_tarfile_keeps_position @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamReadTest.test_is_tarfile_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2StreamReadTest.test_length_zero_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2StreamReadTest.test_length_zero_header @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamReadTest.test_non_existent_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamReadTest.test_null_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamReadTest.test_premature_end_of_archive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -79,12 +79,12 @@ test.test_tarfile.Bz2StreamWriteTest.test_file_mode @ darwin-arm64,linux-aarch64 test.test_tarfile.Bz2StreamWriteTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2StreamWriteTest.test_stream_padding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2UstarReadTest.test_add_dir_getmember @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.Bz2UstarReadTest.test_fileobj_iter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2UstarReadTest.test_fileobj_iter @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2UstarReadTest.test_fileobj_link1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2UstarReadTest.test_fileobj_link2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2UstarReadTest.test_fileobj_link2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2UstarReadTest.test_fileobj_readlines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2UstarReadTest.test_fileobj_regular_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2UstarReadTest.test_fileobj_seek @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2UstarReadTest.test_fileobj_seek @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2UstarReadTest.test_fileobj_symlink1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2UstarReadTest.test_fileobj_symlink2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2UstarReadTest.test_fileobj_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -95,12 +95,12 @@ test.test_tarfile.Bz2WriteTest.test_add_self @ darwin-arm64,linux-aarch64,linux- test.test_tarfile.Bz2WriteTest.test_cwd @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2WriteTest.test_directory_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2WriteTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2WriteTest.test_extractall_symlinks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2WriteTest.test_extractall_symlinks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2WriteTest.test_file_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2WriteTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.Bz2WriteTest.test_fileobj_no_close @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2WriteTest.test_filter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2WriteTest.test_gettarinfo_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.Bz2WriteTest.test_link_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.Bz2WriteTest.test_link_size @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.Bz2WriteTest.test_open_nonwritable_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2WriteTest.test_ordered_recursion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.Bz2WriteTest.test_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -111,12 +111,12 @@ test.test_tarfile.CommandLineTest.test_create_command @ darwin-arm64,linux-aarch test.test_tarfile.CommandLineTest.test_create_command_compressed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.CommandLineTest.test_create_command_dot_started_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.CommandLineTest.test_create_command_dotless_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.CommandLineTest.test_create_command_verbose @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.CommandLineTest.test_create_command_verbose @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.CommandLineTest.test_extract_command @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.CommandLineTest.test_extract_command_different_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.CommandLineTest.test_extract_command_different_directory @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.CommandLineTest.test_extract_command_filter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.CommandLineTest.test_extract_command_invalid_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.CommandLineTest.test_extract_command_verbose @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.CommandLineTest.test_extract_command_verbose @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.CommandLineTest.test_list_command @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.CommandLineTest.test_list_command_invalid_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.CommandLineTest.test_list_command_verbose @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -127,12 +127,12 @@ test.test_tarfile.CompressLevelRaises.test_compresslevel_wrong_modes @ darwin-ar test.test_tarfile.CompressLevelRaises.test_wrong_compresslevels @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ContextManagerTest.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ContextManagerTest.test_closed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.ContextManagerTest.test_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.ContextManagerTest.test_eof @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ContextManagerTest.test_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.ContextManagerTest.test_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.ContextManagerTest.test_fileobj @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ContextManagerTest.test_no_eof @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.CreateTest.test_create @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.CreateTest.test_create_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.CreateTest.test_create_existing @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.CreateTest.test_create_existing_taropen @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.CreateTest.test_create_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.CreateTest.test_create_taropen @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -143,12 +143,12 @@ test.test_tarfile.CreateWithXModeTest.test_create @ darwin-arm64,linux-aarch64,l test.test_tarfile.CreateWithXModeTest.test_create_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.CreateWithXModeTest.test_create_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.CreateWithXModeTest.test_create_taropen_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.CreateWithXModeTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.CreateWithXModeTest.test_eof_marker @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.CreateWithXModeTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.DetectReadTest.test_detect_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.DetectReadTest.test_detect_file @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.DetectReadTest.test_detect_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.DeviceHeaderTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.DeviceHeaderTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.DeviceHeaderTest.test_fileobj_no_close @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.DeviceHeaderTest.test_headers_written_only_for_device_files @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUReadTest.test_header_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUReadTest.test_longname_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -159,12 +159,12 @@ test.test_tarfile.GNUReadTest.test_sparse_file_01 @ darwin-arm64 test.test_tarfile.GNUReadTest.test_sparse_file_10 @ darwin-arm64 test.test_tarfile.GNUReadTest.test_sparse_file_old @ darwin-arm64 test.test_tarfile.GNUReadTest.test_truncated_longname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GNUUnicodeTest.test_bad_pax_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GNUUnicodeTest.test_bad_pax_header @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUUnicodeTest.test_iso8859_1_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GNUUnicodeTest.test_uname_unicode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.GNUUnicodeTest.test_uname_unicode @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.GNUUnicodeTest.test_unicode_argument @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUUnicodeTest.test_unicode_filename_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GNUUnicodeTest.test_utf7_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GNUUnicodeTest.test_utf7_filename @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUUnicodeTest.test_utf8_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUWriteTest.test_longlink_1023 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUWriteTest.test_longlink_1024 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -175,12 +175,12 @@ test.test_tarfile.GNUWriteTest.test_longname_1025 @ darwin-arm64,linux-aarch64,l test.test_tarfile.GNUWriteTest.test_longnamelink_1023 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUWriteTest.test_longnamelink_1024 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GNUWriteTest.test_longnamelink_1025 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzCompressStreamWriteTest.test_compression_levels @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzCompressStreamWriteTest.test_compression_levels @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzCompressWriteTest.test_compression_levels @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipAppendTest.test_append_compressed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipAppendTest.test_append_compressed @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipBrokenHeaderCorrectException.runTest @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipCreateTest.test_create @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipCreateTest.test_create_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipCreateTest.test_create_existing @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipCreateTest.test_create_existing_taropen @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipCreateTest.test_create_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipCreateTest.test_create_taropen @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -191,11 +191,11 @@ test.test_tarfile.GzipCreateTest.test_fileobj_no_close @ darwin-arm64,linux-aarc test.test_tarfile.GzipDetectReadTest.test_detect_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipDetectReadTest.test_detect_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipListTest.test_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipListTest.test_list_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipMiscReadTest.test_bytes_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipListTest.test_list_members @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipMiscReadTest.test_bytes_name_attribute @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_check_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_deprecation_if_no_filter_passed_to_extract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipMiscReadTest.test_deprecation_if_no_filter_passed_to_extractall @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipMiscReadTest.test_deprecation_if_no_filter_passed_to_extractall @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_empty_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_extract_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -206,12 +206,12 @@ test.test_tarfile.GzipMiscReadTest.test_extractall_pathlike_dir @ darwin-arm64,l test.test_tarfile.GzipMiscReadTest.test_extractfile_attrs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_fail_comp @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_fileobj_with_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipMiscReadTest.test_find_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipMiscReadTest.test_find_members @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_ignore_zeros @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipMiscReadTest.test_illegal_mode_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipMiscReadTest.test_illegal_mode_arg @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_init_close_fobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_int_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipMiscReadTest.test_is_tarfile_erroneous @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipMiscReadTest.test_is_tarfile_erroneous @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_is_tarfile_keeps_position @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_is_tarfile_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_length_zero_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -222,12 +222,12 @@ test.test_tarfile.GzipMiscReadTest.test_non_existent_tarfile @ darwin-arm64,linu test.test_tarfile.GzipMiscReadTest.test_null_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_parallel_iteration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_pathlike_bytes_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipMiscReadTest.test_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipMiscReadTest.test_pathlike_name @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_premature_end_of_archive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.GzipMiscReadTest.test_v7_dirtype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipMiscReadTest.test_v7_dirtype @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_xstar_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipMiscReadTest.test_zlib_error_does_not_leak @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipStreamReadTest.test_compare_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipStreamReadTest.test_compare_members @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamReadTest.test_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamReadTest.test_extractfile_attrs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamReadTest.test_fileobj_regular_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -238,12 +238,12 @@ test.test_tarfile.GzipStreamReadTest.test_is_tarfile_valid @ darwin-arm64,linux- test.test_tarfile.GzipStreamReadTest.test_length_zero_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamReadTest.test_non_existent_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamReadTest.test_null_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipStreamReadTest.test_premature_end_of_archive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.GzipStreamReadTest.test_premature_end_of_archive @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.GzipStreamReadTest.test_provoke_stream_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipStreamReadTest.test_read_through @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipStreamReadTest.test_read_through @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamWriteTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamWriteTest.test_file_mode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.GzipStreamWriteTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipStreamWriteTest.test_fileobj_no_close @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamWriteTest.test_source_directory_not_leaked @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipStreamWriteTest.test_stream_padding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipUstarReadTest.test_add_dir_getmember @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -254,12 +254,12 @@ test.test_tarfile.GzipUstarReadTest.test_fileobj_readlines @ darwin-arm64,linux- test.test_tarfile.GzipUstarReadTest.test_fileobj_regular_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipUstarReadTest.test_fileobj_seek @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipUstarReadTest.test_fileobj_symlink1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipUstarReadTest.test_fileobj_symlink2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipUstarReadTest.test_fileobj_symlink2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipUstarReadTest.test_fileobj_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipUstarReadTest.test_issue14160 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipUstarReadTest.test_issue14160 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipWriteTest.test_100_char_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipWriteTest.test_abs_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipWriteTest.test_add_self @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipWriteTest.test_add_self @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipWriteTest.test_cwd @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipWriteTest.test_directory_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipWriteTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -270,12 +270,12 @@ test.test_tarfile.GzipWriteTest.test_filter @ darwin-arm64,linux-aarch64,linux-a test.test_tarfile.GzipWriteTest.test_gettarinfo_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipWriteTest.test_link_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.GzipWriteTest.test_open_nonwritable_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipWriteTest.test_ordered_recursion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.GzipWriteTest.test_ordered_recursion @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.GzipWriteTest.test_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.GzipWriteTest.test_symlink_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.GzipWriteTest.test_symlink_size @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.GzipWriteTest.test_tar_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.HardlinkTest.test_add_hardlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.HardlinkTest.test_add_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.HardlinkTest.test_add_twice @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.HardlinkTest.test_dereference_hardlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.LimitsTest.test_gnu_limits @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LimitsTest.test_pax_limits @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -283,10 +283,10 @@ test.test_tarfile.LimitsTest.test_ustar_limits @ darwin-arm64,linux-aarch64,linu test.test_tarfile.ListTest.test_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ListTest.test_list_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaAppendTest.test_append_compressed @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaCreateTest.test_create @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaCreateTest.test_create @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaCreateTest.test_create_existing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaCreateTest.test_create_existing_taropen @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaCreateTest.test_create_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaCreateTest.test_create_pathlike_name @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaCreateTest.test_create_taropen @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaCreateTest.test_create_taropen_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaCreateTest.test_create_with_preset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -297,10 +297,10 @@ test.test_tarfile.LzmaDetectReadTest.test_detect_fileobj @ darwin-arm64,linux-aa test.test_tarfile.LzmaListTest.test_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaListTest.test_list_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_check_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaMiscReadTest.test_deprecation_if_no_filter_passed_to_extract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaMiscReadTest.test_deprecation_if_no_filter_passed_to_extract @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_deprecation_if_no_filter_passed_to_extractall @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_empty_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaMiscReadTest.test_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaMiscReadTest.test_empty_tarfile @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_extract_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.LzmaMiscReadTest.test_extract_hardlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_extract_pathlike_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -311,12 +311,12 @@ test.test_tarfile.LzmaMiscReadTest.test_fail_comp @ darwin-arm64,linux-aarch64,l test.test_tarfile.LzmaMiscReadTest.test_fileobj_with_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_find_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_ignore_zeros @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaMiscReadTest.test_illegal_mode_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaMiscReadTest.test_illegal_mode_arg @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_init_close_fobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaMiscReadTest.test_int_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaMiscReadTest.test_int_name_attribute @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_is_tarfile_erroneous @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_is_tarfile_keeps_position @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaMiscReadTest.test_is_tarfile_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaMiscReadTest.test_is_tarfile_valid @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_length_zero_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_next_on_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.LzmaMiscReadTest.test_no_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -326,12 +326,12 @@ test.test_tarfile.LzmaMiscReadTest.test_parallel_iteration @ darwin-arm64,linux- test.test_tarfile.LzmaMiscReadTest.test_pathlike_bytes_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_premature_end_of_archive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.LzmaMiscReadTest.test_v7_dirtype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaMiscReadTest.test_v7_dirtype @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaMiscReadTest.test_xstar_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaMiscReadTest.test_zlib_error_does_not_leak @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaMiscReadTest.test_zlib_error_does_not_leak @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaStreamReadTest.test_compare_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaStreamReadTest.test_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaStreamReadTest.test_extractfile_attrs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaStreamReadTest.test_extractfile_attrs @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaStreamReadTest.test_fileobj_regular_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaStreamReadTest.test_ignore_zeros @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaStreamReadTest.test_is_tarfile_erroneous @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -342,12 +342,12 @@ test.test_tarfile.LzmaStreamReadTest.test_non_existent_tarfile @ darwin-arm64,li test.test_tarfile.LzmaStreamReadTest.test_null_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaStreamReadTest.test_premature_end_of_archive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.LzmaStreamReadTest.test_provoke_stream_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaStreamReadTest.test_read_through @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaStreamReadTest.test_read_through @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaStreamWriteTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaStreamWriteTest.test_file_mode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.LzmaStreamWriteTest.test_file_mode @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.LzmaStreamWriteTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaStreamWriteTest.test_stream_padding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaUstarReadTest.test_add_dir_getmember @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.LzmaUstarReadTest.test_add_dir_getmember @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.LzmaUstarReadTest.test_fileobj_iter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaUstarReadTest.test_fileobj_link1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaUstarReadTest.test_fileobj_link2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -358,12 +358,12 @@ test.test_tarfile.LzmaUstarReadTest.test_fileobj_symlink1 @ darwin-arm64,linux-a test.test_tarfile.LzmaUstarReadTest.test_fileobj_symlink2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaUstarReadTest.test_fileobj_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaUstarReadTest.test_issue14160 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaWriteTest.test_100_char_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaWriteTest.test_100_char_name @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaWriteTest.test_abs_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaWriteTest.test_add_self @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaWriteTest.test_add_self @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaWriteTest.test_cwd @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.LzmaWriteTest.test_directory_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.LzmaWriteTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaWriteTest.test_eof_marker @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaWriteTest.test_extractall_symlinks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaWriteTest.test_file_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaWriteTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -374,12 +374,12 @@ test.test_tarfile.LzmaWriteTest.test_open_nonwritable_fileobj @ darwin-arm64,lin test.test_tarfile.LzmaWriteTest.test_ordered_recursion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaWriteTest.test_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.LzmaWriteTest.test_symlink_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.LzmaWriteTest.test_tar_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.LzmaWriteTest.test_tar_size @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_blktype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MemberReadTest.test_find_chrtype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MemberReadTest.test_find_chrtype @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_conttype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_dirtype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MemberReadTest.test_find_dirtype_with_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MemberReadTest.test_find_dirtype_with_size @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_fifotype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_gnusparse @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_gnusparse_00 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -390,12 +390,12 @@ test.test_tarfile.MemberReadTest.test_find_pax_umlauts @ darwin-arm64,linux-aarc test.test_tarfile.MemberReadTest.test_find_regtype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_regtype_oldv7 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_sparse @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MemberReadTest.test_find_symtype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MemberReadTest.test_find_symtype @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MemberReadTest.test_find_umlauts @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MemberReadTest.test_find_ustar_longname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MemberReadTest.test_find_ustar_longname @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_bytes_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_check_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MiscReadTest.test_deprecation_if_no_filter_passed_to_extract @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MiscReadTest.test_deprecation_if_no_filter_passed_to_extract @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_deprecation_if_no_filter_passed_to_extractall @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_empty_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_empty_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -406,12 +406,12 @@ test.test_tarfile.MiscReadTest.test_extractall @ darwin-arm64,linux-aarch64,linu test.test_tarfile.MiscReadTest.test_extractall_pathlike_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_extractfile_attrs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_fileobj_with_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MiscReadTest.test_find_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MiscReadTest.test_find_members @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_ignore_zeros @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.MiscReadTest.test_illegal_mode_arg @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MiscReadTest.test_illegal_mode_arg @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_init_close_fobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_int_name_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MiscReadTest.test_is_tarfile_erroneous @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MiscReadTest.test_is_tarfile_erroneous @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_is_tarfile_keeps_position @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_is_tarfile_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_length_zero_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -422,12 +422,12 @@ test.test_tarfile.MiscReadTest.test_non_existent_tarfile @ darwin-arm64,linux-aa test.test_tarfile.MiscReadTest.test_null_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_parallel_iteration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_pathlike_bytes_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MiscReadTest.test_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MiscReadTest.test_pathlike_name @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_premature_end_of_archive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.MiscReadTest.test_v7_dirtype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MiscReadTest.test_v7_dirtype @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_xstar_type @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscReadTest.test_zlib_error_does_not_leak @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.MiscTest.test__all__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.MiscTest.test__all__ @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscTest.test_char_fields @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscTest.test_number_field_limits @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.MiscTest.test_read_number_fields @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -438,12 +438,12 @@ test.test_tarfile.NoneInfoExtractTests_Data.test_extractall_none_gname @ darwin- test.test_tarfile.NoneInfoExtractTests_Data.test_extractall_none_mode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Data.test_extractall_none_mtime @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Data.test_extractall_none_ownership @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.NoneInfoExtractTests_Data.test_extractall_none_uid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.NoneInfoExtractTests_Data.test_extractall_none_uid @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Data.test_extractall_none_uname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_gid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_gid @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_gname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_mode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_mtime @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_mtime @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_ownership @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_uid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Default.test_extractall_none_uname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -454,12 +454,12 @@ test.test_tarfile.NoneInfoExtractTests_FullyTrusted.test_extractall_none_mtime @ test.test_tarfile.NoneInfoExtractTests_FullyTrusted.test_extractall_none_ownership @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_FullyTrusted.test_extractall_none_uid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_FullyTrusted.test_extractall_none_uname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_gid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_gid @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_gname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_mode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_mode @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_mtime @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_ownership @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_uid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_uid @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoExtractTests_Tar.test_extractall_none_uname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoTests_Misc.test_add @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.NoneInfoTests_Misc.test_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -469,12 +469,12 @@ test.test_tarfile.NumericOwnerTest.test_keyword_only @ darwin-arm64,linux-aarch6 test.test_tarfile.OverwriteTests.test_overwrite_broken_dir_symlink_as_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.OverwriteTests.test_overwrite_broken_dir_symlink_as_implicit_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.OverwriteTests.test_overwrite_broken_file_symlink_as_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.OverwriteTests.test_overwrite_dir_as_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.OverwriteTests.test_overwrite_dir_as_dir @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.OverwriteTests.test_overwrite_dir_as_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.OverwriteTests.test_overwrite_dir_as_implicit_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.OverwriteTests.test_overwrite_dir_as_implicit_dir @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.OverwriteTests.test_overwrite_dir_symlink_as_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.OverwriteTests.test_overwrite_dir_symlink_as_implicit_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.OverwriteTests.test_overwrite_file_as_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.OverwriteTests.test_overwrite_file_as_dir @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.OverwriteTests.test_overwrite_file_as_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.OverwriteTests.test_overwrite_file_as_implicit_dir @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.OverwriteTests.test_overwrite_file_symlink_as_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -485,12 +485,12 @@ test.test_tarfile.PAXUnicodeTest.test_unicode_argument @ darwin-arm64,linux-aarc test.test_tarfile.PAXUnicodeTest.test_utf7_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PAXUnicodeTest.test_utf8_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxReadTest.test_header_offset @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.PaxReadTest.test_longname_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.PaxReadTest.test_longname_directory @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxReadTest.test_pax_global_headers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.PaxReadTest.test_pax_header_bad_formats @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.PaxReadTest.test_pax_header_bad_formats @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxReadTest.test_pax_number_fields @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxReadTest.test_read_longlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.PaxReadTest.test_read_longname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.PaxReadTest.test_read_longname @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxReadTest.test_truncated_longname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxWriteTest.test_create_pax_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxWriteTest.test_longlink_1023 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -501,12 +501,12 @@ test.test_tarfile.PaxWriteTest.test_longname_1024 @ darwin-arm64,linux-aarch64,l test.test_tarfile.PaxWriteTest.test_longname_1025 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxWriteTest.test_longnamelink_1023 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxWriteTest.test_longnamelink_1024 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.PaxWriteTest.test_longnamelink_1025 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.PaxWriteTest.test_longnamelink_1025 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.PaxWriteTest.test_pax_extended_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.PaxWriteTest.test_pax_global_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.PaxWriteTest.test_pax_global_header @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ReplaceTests.test_replace_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ReplaceTests.test_replace_deep @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.ReplaceTests.test_replace_internal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.ReplaceTests.test_replace_internal @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ReplaceTests.test_replace_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.ReplaceTests.test_replace_shallow @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.StreamReadTest.test_compare_members @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -517,12 +517,12 @@ test.test_tarfile.StreamReadTest.test_ignore_zeros @ darwin-arm64,linux-aarch64, test.test_tarfile.StreamReadTest.test_is_tarfile_erroneous @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.StreamReadTest.test_is_tarfile_keeps_position @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.StreamReadTest.test_is_tarfile_valid @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.StreamReadTest.test_length_zero_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.StreamReadTest.test_length_zero_header @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.StreamReadTest.test_non_existent_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.StreamReadTest.test_null_tarfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.StreamReadTest.test_null_tarfile @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.StreamReadTest.test_premature_end_of_archive @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.StreamReadTest.test_provoke_stream_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.StreamReadTest.test_read_through @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.StreamReadTest.test_read_through @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.StreamWriteTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.StreamWriteTest.test_file_mode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.StreamWriteTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -533,12 +533,12 @@ test.test_tarfile.TestExtractionFilters.test_absolute_symlink @ darwin-arm64,lin test.test_tarfile.TestExtractionFilters.test_bad_filter_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.TestExtractionFilters.test_benign_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.TestExtractionFilters.test_chains @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.TestExtractionFilters.test_change_default_filter_on_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.TestExtractionFilters.test_change_default_filter_on_class @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.TestExtractionFilters.test_change_default_filter_on_instance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.TestExtractionFilters.test_change_default_filter_on_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.TestExtractionFilters.test_change_default_filter_on_subclass @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.TestExtractionFilters.test_change_default_filter_to_string @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.TestExtractionFilters.test_custom_filter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_tarfile.TestExtractionFilters.test_data_filter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.TestExtractionFilters.test_data_filter @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.TestExtractionFilters.test_deep_symlink @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.TestExtractionFilters.test_default_filter_warns @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.TestExtractionFilters.test_errorlevel @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -549,48 +549,48 @@ test.test_tarfile.TestExtractionFilters.test_parent_symlink2 @ darwin-arm64,linu test.test_tarfile.TestExtractionFilters.test_pipe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.TestExtractionFilters.test_sly_relative0 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.TestExtractionFilters.test_sly_relative2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.TestExtractionFilters.test_special_files @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_tarfile.TestExtractionFilters.test_special_files @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.TestExtractionFilters.test_stateful_filter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.TestExtractionFilters.test_tar_filter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_tarfile.TestExtractionFilters.test_tar_filter @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.UstarReadTest.test_add_dir_getmember @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.UstarReadTest.test_fileobj_iter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_fileobj_link1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_fileobj_link2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_fileobj_readlines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_fileobj_regular_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_fileobj_seek @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_fileobj_symlink1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_fileobj_symlink2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_fileobj_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarReadTest.test_issue14160 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_iso8859_1_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_tarfile.UstarReadTest.test_fileobj_iter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_fileobj_link1 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_fileobj_link2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_fileobj_readlines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_fileobj_regular_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_fileobj_seek @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_fileobj_symlink1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_fileobj_symlink2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_fileobj_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarReadTest.test_issue14160 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_iso8859_1_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.UstarUnicodeTest.test_uname_unicode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.UstarUnicodeTest.test_unicode_argument @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_unicode_filename_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_unicode_link1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_tarfile.UstarUnicodeTest.test_unicode_argument @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_unicode_filename_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_unicode_link1 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_tarfile.UstarUnicodeTest.test_unicode_link2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.UstarUnicodeTest.test_unicode_longname1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_unicode_longname2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_unicode_longname3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_unicode_longname4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_unicode_name1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_unicode_name2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_utf7_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.UstarUnicodeTest.test_utf8_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_100_char_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_abs_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_add_self @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_cwd @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_directory_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_extractall_symlinks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_file_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_filter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_gettarinfo_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_tarfile.UstarUnicodeTest.test_unicode_longname1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_unicode_longname2 @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_unicode_longname3 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_unicode_longname4 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_unicode_name1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_unicode_name2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_utf7_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.UstarUnicodeTest.test_utf8_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_100_char_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_abs_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_add_self @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_cwd @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_directory_size @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_eof_marker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_extractall_symlinks @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_file_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_fileobj_no_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_filter @ linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_gettarinfo_pathlike_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.WriteTest.test_link_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.WriteTest.test_open_nonwritable_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_ordered_recursion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_tarfile.WriteTest.test_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_tarfile.WriteTest.test_open_nonwritable_fileobj @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_ordered_recursion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_tarfile.WriteTest.test_pathnames @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_tarfile.WriteTest.test_symlink_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_tarfile.WriteTest.test_tar_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_tarfile.WriteTest.test_tar_size @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_threading.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_threading.txt index ff4f798b4a..2ebe1b9bce 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_threading.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_threading.txt @@ -125,8 +125,7 @@ test.test_threading.SemaphoreTests.test_repr @ darwin-arm64,linux-aarch64,linux- test.test_threading.SemaphoreTests.test_try_acquire @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_threading.SemaphoreTests.test_try_acquire_contended @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_threading.SemaphoreTests.test_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# Can transiently fail with java.lang.AssertionError: The TruffleContext must be entered -!test.test_threading.ThreadJoinOnShutdown.test_1_join_on_shutdown +test.test_threading.ThreadJoinOnShutdown.test_1_join_on_shutdown @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_threading.ThreadJoinOnShutdown.test_4_daemon_threads @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_threading.ThreadJoinOnShutdown.test_thread_from_thread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_threading.ThreadTests.test_BoundedSemaphore_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_trace.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_trace.txt index 1cf5a88bf5..df90cd3ea5 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_trace.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_trace.txt @@ -1,4 +1,6 @@ +test.test_trace.TestCommandLine.test_count_and_summary @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_trace.TestCommandLine.test_failures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_trace.TestCommandLine.test_listfuncs_flag_success @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_trace.TestCommandLine.test_run_as_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_trace.TestCommandLine.test_sys_argv_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_trace.TestCoverage.test_coverage @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -13,9 +15,8 @@ test.test_trace.TestFuncs.test_simple_caller @ darwin-arm64,linux-aarch64,linux- test.test_trace.TestFuncs.test_traced_decorated_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_trace.TestLineCounts.test_linear_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_trace.TestLineCounts.test_trace_func_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71865 -!test.test_trace.TestLineCounts.test_trace_list_comprehension @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 -!test.test_trace.TestLineCounts.test_traced_decorated_function @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_trace.TestLineCounts.test_trace_list_comprehension @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_trace.TestLineCounts.test_traced_decorated_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_trace.TestLineCounts.test_traced_func_importing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_trace.TestLineCounts.test_traced_func_linear @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_trace.TestLineCounts.test_traced_func_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_traceback.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_traceback.txt index 80774e10fd..201a93f5f4 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_traceback.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_traceback.txt @@ -18,15 +18,15 @@ test.test_traceback.PurePythonSuggestionFormattingTests.test_import_from_error_w test.test_traceback.PurePythonSuggestionFormattingTests.test_import_from_suggestions_do_not_trigger_for_big_namespaces @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonSuggestionFormattingTests.test_import_from_suggestions_do_not_trigger_for_long_attributes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_bad_suggestions_do_not_trigger_for_small_names @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_for_private_stdlib_modules @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_for_stdlib_modules @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_for_private_stdlib_modules @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_for_stdlib_modules @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_suggestions_do_not_trigger_for_long_names @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_suggestions_do_not_trigger_for_too_many_locals @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_suggestions_from_builtins @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_suggestions_from_builtins_when_builtins_is_module @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_suggestions_from_globals @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_suggestions_from_builtins @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_suggestions_from_builtins_when_builtins_is_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_suggestions_from_globals @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_with_custom_exceptions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_with_instance @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_traceback.PurePythonSuggestionFormattingTests.test_name_error_with_instance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonSuggestionFormattingTests.test_unbound_local_error_does_not_match @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonSuggestionFormattingTests.test_unbound_local_error_with_instance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonTracebackErrorCaretTests.test_basic_caret @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -40,8 +40,7 @@ test.test_traceback.PurePythonTracebackErrorCaretTests.test_caret_for_subscript_ test.test_traceback.PurePythonTracebackErrorCaretTests.test_caret_in_type_annotation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonTracebackErrorCaretTests.test_caret_multiline_expression @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonTracebackErrorCaretTests.test_caret_multiline_expression_bin_op @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -# GR-71889 -!test.test_traceback.PurePythonTracebackErrorCaretTests.test_decorator_application_lineno_correct @ darwin-arm64,linux-aarch64,linux-x86_64,win32-AMD64 +test.test_traceback.PurePythonTracebackErrorCaretTests.test_decorator_application_lineno_correct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonTracebackErrorCaretTests.test_secondary_caret_not_elided @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonTracebackErrorCaretTests.test_specialization_variations @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.PurePythonTracebackErrorCaretTests.test_traceback_specialization_with_syntax_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -101,7 +100,7 @@ test.test_traceback.TestTracebackException.test_context @ darwin-arm64,linux-aar test.test_traceback.TestTracebackException.test_from_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.TestTracebackException.test_limit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.TestTracebackException.test_locals @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_traceback.TestTracebackException.test_long_context_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_traceback.TestTracebackException.test_long_context_chain @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_traceback.TestTracebackException.test_lookup_lines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.TestTracebackException.test_no_locals @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.TestTracebackException.test_no_refs_to_exception_and_traceback_objects @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -132,4 +131,6 @@ test.test_traceback.TracebackFormatTests.test_exception_group_deep_recursion_tra test.test_traceback.TracebackFormatTests.test_format_stack @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.TracebackFormatTests.test_print_exception_bad_type_python @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_traceback.TracebackFormatTests.test_print_stack @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +# Relies on recursion limit +!test.test_traceback.TracebackFormatTests.test_recursive_traceback_python test.test_traceback.TracebackFormatTests.test_stack_format @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_type_params.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_type_params.txt index 611f5b7385..8473288ca4 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_type_params.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_type_params.txt @@ -80,7 +80,7 @@ test.test_type_params.TypeParamsTypeParamsDunder.test_typeparams_dunder_function test.test_type_params.TypeParamsTypeVarParamSpecTest.test_paramspec_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_type_params.TypeParamsTypeVarParamSpecTest.test_paramspec_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_type_params.TypeParamsTypeVarTest.test_typevar_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_type_params.TypeParamsTypeVarTest.test_typevar_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_type_params.TypeParamsTypeVarTest.test_typevar_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_type_params.TypeParamsTypeVarTest.test_typevar_generator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_type_params.TypeParamsTypeVarTupleTest.test_typevartuple_01 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_type_params.TypeParamsTypeVarTupleTest.test_typevartuple_02 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_typing.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_typing.txt index 2db8871274..6047a838ee 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_typing.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_typing.txt @@ -163,6 +163,7 @@ test.test_typing.ForwardRefTests.test_no_type_check @ darwin-arm64,linux-aarch64 test.test_typing.ForwardRefTests.test_no_type_check_TypeError @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_typing.ForwardRefTests.test_no_type_check_class @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_typing.ForwardRefTests.test_no_type_check_class_and_static_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_typing.ForwardRefTests.test_no_type_check_foreign_functions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_typing.ForwardRefTests.test_no_type_check_forward_ref_as_string @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_typing.ForwardRefTests.test_no_type_check_lambda @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_typing.ForwardRefTests.test_no_type_check_nested_types @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ucn.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ucn.txt index 54716cabb8..5bd8f6ca2f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ucn.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_ucn.txt @@ -10,4 +10,5 @@ test.test_ucn.UnicodeNamesTest.test_misc_symbols @ darwin-arm64,linux-aarch64,li # Accesses http://www.pythontest.net !test.test_ucn.UnicodeNamesTest.test_named_sequences_full test.test_ucn.UnicodeNamesTest.test_named_sequences_names_in_pua_range @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_ucn.UnicodeNamesTest.test_named_sequences_sample @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_ucn.UnicodeNamesTest.test_strict_error_handling @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicode.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicode.txt index ecac13ef77..49b757e413 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicode.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicode.txt @@ -2,6 +2,7 @@ test.test_unicode.StringModuleTest.test_formatter_field_name_split @ darwin-arm6 test.test_unicode.StringModuleTest.test_formatter_parser @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unicode.StringModuleTest.test_str_subclass_attr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unicode.UnicodeTest.test___contains__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_unicode.UnicodeTest.test_adaptive_find @ darwin-arm64 test.test_unicode.UnicodeTest.test_additional_rsplit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unicode.UnicodeTest.test_additional_split @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unicode.UnicodeTest.test_ascii @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicodedata.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicodedata.txt index 2db9249d87..6022b6dca8 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicodedata.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unicodedata.txt @@ -1,6 +1,6 @@ test.test_unicodedata.NormalizationTest.test_bug_834676 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unicodedata.NormalizationTest.test_edge_cases @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unicodedata.NormalizationTest.test_normalization @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_unicodedata.NormalizationTest.test_normalization @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_unicodedata.UnicodeFunctionsTest.test_category @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unicodedata.UnicodeFunctionsTest.test_combining @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unicodedata.UnicodeFunctionsTest.test_decimal @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unittest.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unittest.txt index 82eb3b9f22..e6ec75dfe0 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unittest.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_unittest.txt @@ -32,19 +32,19 @@ test.test_unittest.test_assertions.Test_Assertions.test_AlmostEqual @ darwin-arm test.test_unittest.test_assertions.Test_Assertions.test_AmostEqualWithDelta @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_assertions.Test_Assertions.test_assertRaises @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_assertions.Test_Assertions.test_assertRaises_frames_survival @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.test_async_case.TestAsyncCase.test_base_exception_from_async_method @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_cancellation_hanging_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_cleanups_interleave_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_debug_cleanup_same_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_deprecation_of_return_val_from_test @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_enterAsyncContext @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_enterAsyncContext_arg_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_exception_in_setup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_exception_in_tear_clean_up @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_exception_in_tear_down @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_exception_in_test @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_full_cycle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.test_async_case.TestAsyncCase.test_setup_get_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.test_async_case.TestAsyncCase.test_base_exception_from_async_method @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_cancellation_hanging_tasks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_cleanups_interleave_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_debug_cleanup_same_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_deprecation_of_return_val_from_test @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_enterAsyncContext @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_enterAsyncContext_arg_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_exception_in_setup @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_exception_in_tear_clean_up @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_exception_in_tear_down @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_exception_in_test @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_full_cycle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.test_async_case.TestAsyncCase.test_setup_get_event_loop @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.test_case.Test_TestCase.testAddTypeEqualityFunc @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_case.Test_TestCase.testAssertCountEqual @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_case.Test_TestCase.testAssertDictEqualTruncates @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -211,7 +211,7 @@ test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_ test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_TestSuite @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_bad_object @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_empty_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_invalid_testmethod @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_invalid_testmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_malformed_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_not_a_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromName__relative_testmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -233,7 +233,7 @@ test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative_bad_object @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative_empty_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative_empty_name_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative_invalid_testmethod @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative_invalid_testmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative_malformed_name @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative_not_a_module @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_loader.Test_TestLoader.test_loadTestsFromNames__relative_testmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -468,74 +468,74 @@ test.test_unittest.test_suite.Test_TestSuite.test_run__requires_result @ darwin- test.test_unittest.test_util.TestUtil.test_safe_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_util.TestUtil.test_sorted_list_difference @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.test_util.TestUtil.test_unorderable_list_difference @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_add_return_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_exception_iterable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_iterable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_normal_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_await_args_list_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_return_value_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_return_value_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_side_effect_awaitable_values @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_side_effect_is_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_wraps_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncArguments.test_wraps_normal_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncAutospecTest.test_create_autospec @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncArguments.test_add_return_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_exception_iterable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_iterable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_add_side_effect_normal_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_await_args_list_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_return_value_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_return_value_awaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_side_effect_awaitable_values @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_side_effect_is_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_wraps_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncArguments.test_wraps_normal_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncAutospecTest.test_create_autospec @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncAutospecTest.test_create_autospec_instance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncAutospecTest.test_is_AsyncMock_patch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncAutospecTest.test_patch_with_autospec @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_context_manager_raise_exception_by_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_mock_customize_async_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_mock_customize_async_context_manager_with_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_mock_supports_async_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_set_return_value_of_aenter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncIteratorTest.test_aiter_set_return_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncAutospecTest.test_patch_with_autospec @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_context_manager_raise_exception_by_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_mock_customize_async_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_mock_customize_async_context_manager_with_coroutine @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_mock_supports_async_context_manager @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncContextManagerTest.test_set_return_value_of_aenter @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncIteratorTest.test_aiter_set_return_value @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncIteratorTest.test_mock_aiter_and_anext_asyncmock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncIteratorTest.test_mock_async_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncIteratorTest.test_mock_async_for @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncMagicMethods.test_async_magic_methods_return_async_mocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncMagicMethods.test_asyncmock_has_sync_magic_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncMagicMethods.test_magic_methods_are_async_functions @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncMagicMethods.test_magicmock_has_async_magic_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncMagicMethods.test_sync_magic_methods_return_magic_mocks @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_any_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited_but_not_called @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited_once_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_called_and_awaited_at_same_time @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_called_once_and_awaited_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_called_then_awaited @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_has_awaits_no_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_has_awaits_not_matching_spec_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_has_awaits_ordered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_not_awaited @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_awaits_asserts_with_any @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockAssert.test_awaits_asserts_with_spec_and_any @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockTest.test_future_isfuture @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncMockTest.test_isawaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_any_wait @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited_but_not_called @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited_once @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited_once_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_awaited_with @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_called_and_awaited_at_same_time @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_called_once_and_awaited_twice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_called_then_awaited @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_has_awaits_no_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_has_awaits_not_matching_spec_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_has_awaits_ordered @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_assert_not_awaited @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_awaits_asserts_with_any @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockAssert.test_awaits_asserts_with_spec_and_any @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockTest.test_future_isfuture @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncMockTest.test_isawaitable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncMockTest.test_iscoroutinefunction_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncMockTest.test_iscoroutinefunction_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncMockTest.test_iscoroutinefunction_normal_function @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_async_def_cm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_async_def_cm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_is_AsyncMock_cm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_is_async_cm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_is_async_cm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_is_async_function_cm @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_patch_dict_async_def @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_patch_dict_async_def_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncPatchDecoratorTest.test_async_def_patch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_patch_dict_async_def @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncPatchCMTest.test_patch_dict_async_def_context @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncPatchDecoratorTest.test_async_def_patch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncPatchDecoratorTest.test_is_AsyncMock_patch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncPatchDecoratorTest.test_is_AsyncMock_patch_classmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncPatchDecoratorTest.test_is_AsyncMock_patch_staticmethod @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncPatchDecoratorTest.test_is_async_patch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncPatchDecoratorTest.test_is_async_patch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncPatchDecoratorTest.test_is_coroutine_function_patch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncSpecSetTest.test_is_AsyncMock_patch @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncSpecSetTest.test_is_async_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncSpecSetTest.test_is_child_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncSpecSetTest.test_magicmock_lambda_spec @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_unittest.testmock.testasync.AsyncSpecTest.test_spec_as_normal_kw_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_unittest.testmock.testasync.AsyncSpecTest.test_spec_as_normal_positional_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_unittest.testmock.testasync.AsyncSpecTest.test_spec_as_normal_kw_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_unittest.testmock.testasync.AsyncSpecTest.test_spec_as_normal_positional_AsyncMock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_unittest.testmock.testasync.AsyncSpecTest.test_spec_async_attributes @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncSpecTest.test_spec_async_attributes_instance @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_unittest.testmock.testasync.AsyncSpecTest.test_spec_async_mock @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib.txt index 838fbe6fd0..a566fe51cd 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib.txt @@ -75,8 +75,8 @@ test.test_urllib.urlopen_HttpTests.test_cafile_and_context @ darwin-arm64,linux- test.test_urllib.urlopen_HttpTests.test_empty_socket @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_file_notexists @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_ftp_cache_pruning @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib.urlopen_HttpTests.test_ftp_nohost @ linux-aarch64-github,linux-x86_64-github -test.test_urllib.urlopen_HttpTests.test_ftp_nonexisting @ linux-aarch64-github,linux-x86_64-github +test.test_urllib.urlopen_HttpTests.test_ftp_nohost @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_urllib.urlopen_HttpTests.test_ftp_nonexisting @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_invalid_redirect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_missing_localfile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_read_0_9 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -85,8 +85,8 @@ test.test_urllib.urlopen_HttpTests.test_read_1_1 @ darwin-arm64,linux-aarch64,li test.test_urllib.urlopen_HttpTests.test_read_bogus @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_redirect_limit_independent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_url_fragment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib.urlopen_HttpTests.test_url_host_with_control_char_rejected @ linux-aarch64-github,linux-x86_64-github -test.test_urllib.urlopen_HttpTests.test_url_host_with_newline_header_injection_rejected @ linux-aarch64-github,linux-x86_64-github +test.test_urllib.urlopen_HttpTests.test_url_host_with_control_char_rejected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_urllib.urlopen_HttpTests.test_url_host_with_newline_header_injection_rejected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_url_path_with_control_char_rejected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_url_path_with_newline_header_injection_rejected @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib.urlopen_HttpTests.test_userpass_inurl @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib2.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib2.txt index ab8654b76a..40036a8c82 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib2.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib2.txt @@ -33,9 +33,9 @@ test.test_urllib2.HandlerTests.test_proxy_https_proxy_authorization @ darwin-arm test.test_urllib2.HandlerTests.test_proxy_no_proxy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib2.HandlerTests.test_proxy_no_proxy_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib2.HandlerTests.test_redirect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib2.HandlerTests.test_redirect_encoding @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllib2.HandlerTests.test_redirect_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib2.HandlerTests.test_redirect_fragment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib2.HandlerTests.test_redirect_no_path @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllib2.HandlerTests.test_redirect_no_path @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib2.HandlerTests.test_relative_redirect @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib2.HandlerTests.test_unsupported_auth_basic_handler @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_urllib2.HandlerTests.test_unsupported_auth_digest_handler @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib2net.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib2net.txt index 3d4934a2c6..dc88af3c74 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib2net.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllib2net.txt @@ -1,15 +1,15 @@ -test.test_urllib2net.CloseSocketTest.test_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib2net.OtherNetworkTests.test_custom_headers @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_urllib2net.CloseSocketTest.test_close @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllib2net.OtherNetworkTests.test_custom_headers @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_urllib2net.OtherNetworkTests.test_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github # Connecting to external page that sometimes times out !test.test_urllib2net.OtherNetworkTests.test_ftp -test.test_urllib2net.OtherNetworkTests.test_redirect_url_withfrag @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib2net.OtherNetworkTests.test_urlwithfrag @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_urllib2net.OtherNetworkTests.test_redirect_url_withfrag @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllib2net.OtherNetworkTests.test_urlwithfrag @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_urllib2net.TimeoutTest.test_ftp_basic @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_urllib2net.TimeoutTest.test_ftp_default_timeout @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_urllib2net.TimeoutTest.test_ftp_no_timeout @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_urllib2net.TimeoutTest.test_ftp_timeout @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github -test.test_urllib2net.TimeoutTest.test_http_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib2net.TimeoutTest.test_http_default_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib2net.TimeoutTest.test_http_no_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllib2net.TimeoutTest.test_http_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_urllib2net.TimeoutTest.test_http_basic @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllib2net.TimeoutTest.test_http_default_timeout @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllib2net.TimeoutTest.test_http_no_timeout @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllib2net.TimeoutTest.test_http_timeout @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllibnet.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllibnet.txt index a4c7f395e0..f4da4466a0 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllibnet.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_urllibnet.txt @@ -1,12 +1,12 @@ -test.test_urllibnet.URLTimeoutTest.testURLread @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_urllibnet.URLTimeoutTest.testURLread @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github test.test_urllibnet.urlopenNetworkTests.test_bad_address @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlopenNetworkTests.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlopenNetworkTests.test_getcode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlopenNetworkTests.test_geturl @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlopenNetworkTests.test_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlopenNetworkTests.test_readlines @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlretrieveNetworkTests.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlretrieveNetworkTests.test_data_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlretrieveNetworkTests.test_header @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlretrieveNetworkTests.test_reporthook @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_urllibnet.urlretrieveNetworkTests.test_specified_path @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_urllibnet.urlopenNetworkTests.test_basic @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlopenNetworkTests.test_getcode @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlopenNetworkTests.test_geturl @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlopenNetworkTests.test_info @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlopenNetworkTests.test_readlines @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlretrieveNetworkTests.test_basic @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlretrieveNetworkTests.test_data_header @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlretrieveNetworkTests.test_header @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlretrieveNetworkTests.test_reporthook @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github +test.test_urllibnet.urlretrieveNetworkTests.test_specified_path @ linux-aarch64-github,linux-x86_64-github,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_userstring.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_userstring.txt index ee1cc0e8a0..1fa670ec03 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_userstring.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_userstring.txt @@ -1,4 +1,5 @@ test.test_userstring.UserStringTest.test___contains__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_userstring.UserStringTest.test_adaptive_find @ darwin-arm64 test.test_userstring.UserStringTest.test_additional_rsplit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_userstring.UserStringTest.test_additional_split @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_userstring.UserStringTest.test_capitalize @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_uuid.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_uuid.txt index 78f72aad21..2bad4df227 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_uuid.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_uuid.txt @@ -1,4 +1,3 @@ -test.test_uuid.TestInternalsWithoutExtModule.test_arp_getnode @ darwin-arm64 test.test_uuid.TestInternalsWithoutExtModule.test_find_mac_near_keyword @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_uuid.TestInternalsWithoutExtModule.test_find_under_heading @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_uuid.TestInternalsWithoutExtModule.test_find_under_heading_ipv6 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_venv.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_venv.txt index 2ef0dce2eb..56e30acc8c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_venv.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_venv.txt @@ -21,7 +21,7 @@ test.test_venv.BasicTest.test_sysconfig_symlinks @ darwin-arm64,linux-aarch64,li test.test_venv.BasicTest.test_unoverwritable_fails @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_venv.BasicTest.test_upgrade @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_venv.BasicTest.test_upgrade_dependencies @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_venv.BasicTest.test_venv_same_path @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_venv.BasicTest.test_venv_same_path @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 test.test_venv.EnsurePipTest.test_devnull @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_venv.EnsurePipTest.test_explicit_no_pip @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_venv.EnsurePipTest.test_no_pip_by_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_winapi.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_winapi.txt new file mode 100644 index 0000000000..355eac41ad --- /dev/null +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_winapi.txt @@ -0,0 +1,3 @@ +test.test_winapi.WinAPITests.test_getlongpathname @ win32-AMD64 +test.test_winapi.WinAPITests.test_getshortpathname @ win32-AMD64 +test.test_winapi.WinAPITests.test_namedpipe @ win32-AMD64 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_xml_etree_c.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_xml_etree_c.txt index 1875fd6516..4a4a468723 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_xml_etree_c.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_xml_etree_c.txt @@ -1,187 +1,187 @@ -test.test_xml_etree_c.BadElementPathTest.test_find_with_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementPathTest.test_find_with_mutating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementPathTest.test_findall_with_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementPathTest.test_findall_with_mutating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementPathTest.test_findtext_with_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementPathTest.test_findtext_with_falsey_text_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementPathTest.test_findtext_with_mutating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementPathTest.test_findtext_with_none_text_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_ass_subscr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_element_get_tail @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_element_get_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_extend_mutable_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_extend_mutable_list2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_recursive_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_remove_with_mutating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_subscr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_treebuilder_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BadElementTest.test_treebuilder_start @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BasicElementTest.test___copy__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BasicElementTest.test___deepcopy__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BasicElementTest.test___init__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BasicElementTest.test_augmentation_type_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_xml_etree_c.BadElementPathTest.test_find_with_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementPathTest.test_find_with_mutating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementPathTest.test_findall_with_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementPathTest.test_findall_with_mutating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementPathTest.test_findtext_with_error @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementPathTest.test_findtext_with_falsey_text_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementPathTest.test_findtext_with_mutating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementPathTest.test_findtext_with_none_text_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_ass_subscr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_element_get_tail @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_element_get_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_extend_mutable_list @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_extend_mutable_list2 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_recursive_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_remove_with_mutating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_subscr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_treebuilder_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BadElementTest.test_treebuilder_start @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BasicElementTest.test___copy__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BasicElementTest.test___deepcopy__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BasicElementTest.test___init__ @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BasicElementTest.test_augmentation_type_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github !test.test_xml_etree_c.BasicElementTest.test_cyclic_gc -test.test_xml_etree_c.BasicElementTest.test_get_keyword_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BasicElementTest.test_pickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BasicElementTest.test_pickle_issue18997 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BasicElementTest.test_weakref @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BoolTest.test_warning @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_39495_treebuilder_start @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_1534630 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_200708_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_200708_newline @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_200709_default_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_200709_element_comment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_200709_element_insert @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_200709_iter_comment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_200709_register_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit21 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit25 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit28 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit39 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit54 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit55 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit60 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit62 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit63 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_bug_xmltoolkitX1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_expat224_utf8_bug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_expat224_utf8_bug_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_issue10777 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_issue123213_correct_extend_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_issue6233 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_issue6565 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_lost_tail @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.BugsTest.test_lost_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.C14NTest.test_c14n_exclusion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.C14NTest.test_simple_roundtrip @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementFindTest.test_bad_find @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementFindTest.test_find_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementFindTest.test_find_through_ElementTree @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementFindTest.test_find_xpath @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementFindTest.test_findall @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementFindTest.test_findall_different_nsmaps @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementFindTest.test_findall_wildcard @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementFindTest.test_test_find_with_ns @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementIterTest.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementIterTest.test_copy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementIterTest.test_corners @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementIterTest.test_iter_by_tag @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementIterTest.test_pickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementSlicingTest.test_delslice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementSlicingTest.test_getslice_negative_steps @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementSlicingTest.test_getslice_range @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementSlicingTest.test_getslice_single_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementSlicingTest.test_getslice_steps @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementSlicingTest.test_issue123213_setslice_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementSlicingTest.test_setslice_range @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementSlicingTest.test_setslice_single_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_attlist_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_attrib @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_cdata @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_children @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_copy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_custom_builder @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_custom_builder_only_end_ns @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_doctype_public @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_dump_attribute_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_entity @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_file_init @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_html_empty_elems_serialization @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_indent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_indent_level @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_indent_space @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_indent_space_caching @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_initialize_parser_without_target @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_interface @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_issue18347 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_iterparse @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_makeelement @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_parsefile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_parseliteral @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_path_cache @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_processinginstruction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_qname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_set_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostring_default_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostring_default_namespace_different_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostring_default_namespace_original_no_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostring_no_xml_declaration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostring_xml_declaration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostring_xml_declaration_cases @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostring_xml_declaration_unicode_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostringlist_default_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tostringlist_xml_declaration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_tree_write_attribute_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_writefile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_writestring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTest.test_xpath_tokenizer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTypeTest.test_Element_subclass_constructor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTypeTest.test_Element_subclass_find @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTypeTest.test_Element_subclass_new_method @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTypeTest.test_Element_subclass_trivial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ElementTreeTypeTest.test_istype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_read_from_bytesio @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_read_from_stringio @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_read_from_user_binary_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_read_from_user_text_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_short_empty_elements @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_tostringlist_invariant @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_binary_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_binary_file_with_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_bytesio @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_filename_as_unicode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_filename_with_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_stringio @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_text_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_user_binary_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.IOTest.test_write_to_user_text_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.KeywordArgsTest.test_issue14818 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ModuleTest.test_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ModuleTest.test_sanity @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.NamespaceParseTest.test_find_with_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.NoAcceleratorTest.test_correct_import_pyET @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ParseErrorTest.test_error_code @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ParseErrorTest.test_error_position @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.ParseErrorTest.test_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_builder_lookup_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_doctype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_dummy_builder @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_element_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_element_factory_pure_python_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_element_factory_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_late_tail @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_late_tail_mix_pi_comments @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_subclass_comment_pi @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_treebuilder_comment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_treebuilder_elementfactory_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.TreeBuilderTest.test_treebuilder_pi @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XIncludeTest.test_xinclude @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XIncludeTest.test_xinclude_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XIncludeTest.test_xinclude_failures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XIncludeTest.test_xinclude_repeated @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLParserTest.test_constructor_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLParserTest.test_doctype_warning @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLParserTest.test_inherited_doctype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLParserTest.test_parse_string @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLParserTest.test_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLParserTest.test_subclass_doctype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_events @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_events_comment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_events_pi @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_events_sequence @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_feed_while_iterating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_flush_reparse_deferral_disabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_ns_events @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_ns_events_start @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_ns_events_start_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_simple_xml @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_simple_xml_chunk_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_simple_xml_chunk_22 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_simple_xml_chunk_5 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_simple_xml_with_ns @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 -test.test_xml_etree_c.XMLPullParserTest.test_unknown_event @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64 +test.test_xml_etree_c.BasicElementTest.test_get_keyword_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BasicElementTest.test_pickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BasicElementTest.test_pickle_issue18997 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BasicElementTest.test_weakref @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BoolTest.test_warning @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_39495_treebuilder_start @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_1534630 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_200708_close @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_200708_newline @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_200709_default_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_200709_element_comment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_200709_element_insert @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_200709_iter_comment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_200709_register_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit21 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit25 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit28 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit39 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit54 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit55 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit60 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit62 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkit63 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_bug_xmltoolkitX1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_expat224_utf8_bug @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_expat224_utf8_bug_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_issue10777 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_issue123213_correct_extend_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_issue6233 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_issue6565 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_lost_tail @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.BugsTest.test_lost_text @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.C14NTest.test_c14n_exclusion @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.C14NTest.test_simple_roundtrip @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementFindTest.test_bad_find @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementFindTest.test_find_simple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementFindTest.test_find_through_ElementTree @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementFindTest.test_find_xpath @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementFindTest.test_findall @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementFindTest.test_findall_different_nsmaps @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementFindTest.test_findall_wildcard @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementFindTest.test_test_find_with_ns @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementIterTest.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementIterTest.test_copy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementIterTest.test_corners @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementIterTest.test_iter_by_tag @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementIterTest.test_pickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementSlicingTest.test_delslice @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementSlicingTest.test_getslice_negative_steps @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementSlicingTest.test_getslice_range @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementSlicingTest.test_getslice_single_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementSlicingTest.test_getslice_steps @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementSlicingTest.test_issue123213_setslice_exception @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementSlicingTest.test_setslice_range @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementSlicingTest.test_setslice_single_index @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_attlist_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_attrib @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_cdata @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_children @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_copy @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_custom_builder @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_custom_builder_only_end_ns @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_doctype_public @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_dump_attribute_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_entity @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_file_init @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_html_empty_elems_serialization @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_indent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_indent_level @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_indent_space @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_indent_space_caching @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_initialize_parser_without_target @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_interface @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_issue18347 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_iterparse @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_makeelement @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_methods @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_parsefile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_parseliteral @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_path_cache @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_processinginstruction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_qname @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_set_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostring_default_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostring_default_namespace_different_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostring_default_namespace_original_no_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostring_no_xml_declaration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostring_xml_declaration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostring_xml_declaration_cases @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostring_xml_declaration_unicode_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostringlist_default_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tostringlist_xml_declaration @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_tree_write_attribute_order @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_writefile @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_writestring @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTest.test_xpath_tokenizer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTypeTest.test_Element_subclass_constructor @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTypeTest.test_Element_subclass_find @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTypeTest.test_Element_subclass_new_method @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTypeTest.test_Element_subclass_trivial @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ElementTreeTypeTest.test_istype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_read_from_bytesio @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_read_from_stringio @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_read_from_user_binary_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_read_from_user_text_reader @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_short_empty_elements @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_tostringlist_invariant @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_binary_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_binary_file_with_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_bytesio @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_filename_as_unicode @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_filename_with_encoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_stringio @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_text_file @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_user_binary_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.IOTest.test_write_to_user_text_writer @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.KeywordArgsTest.test_issue14818 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ModuleTest.test_all @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ModuleTest.test_sanity @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.NamespaceParseTest.test_find_with_namespace @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.NoAcceleratorTest.test_correct_import_pyET @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ParseErrorTest.test_error_code @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ParseErrorTest.test_error_position @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.ParseErrorTest.test_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_builder_lookup_errors @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_doctype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_dummy_builder @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_element_factory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_element_factory_pure_python_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_element_factory_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_late_tail @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_late_tail_mix_pi_comments @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_subclass_comment_pi @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_treebuilder_comment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_treebuilder_elementfactory_none @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.TreeBuilderTest.test_treebuilder_pi @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XIncludeTest.test_xinclude @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XIncludeTest.test_xinclude_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XIncludeTest.test_xinclude_failures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XIncludeTest.test_xinclude_repeated @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLParserTest.test_constructor_args @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLParserTest.test_doctype_warning @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLParserTest.test_inherited_doctype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLParserTest.test_parse_string @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLParserTest.test_subclass @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLParserTest.test_subclass_doctype @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_events @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_events_comment @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_events_pi @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_events_sequence @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_feed_while_iterating @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_flush_reparse_deferral_disabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_ns_events @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_ns_events_start @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_ns_events_start_end @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_simple_xml @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_simple_xml_chunk_1 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_simple_xml_chunk_22 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_simple_xml_chunk_5 @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_simple_xml_with_ns @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xml_etree_c.XMLPullParserTest.test_unknown_event @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_xmlrpc.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_xmlrpc.txt index 54b0a69083..ece3e8c559 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_xmlrpc.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_xmlrpc.txt @@ -12,8 +12,8 @@ test.test_xmlrpc.DateTimeTestCase.test_time @ darwin-arm64,linux-aarch64,linux-a test.test_xmlrpc.DateTimeTestCase.test_time_struct @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_xmlrpc.DateTimeTestCase.test_time_tuple @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_xmlrpc.FailingServerTestCase.test_basic @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_xmlrpc.FailingServerTestCase.test_fail_no_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_xmlrpc.FailingServerTestCase.test_fail_with_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github +test.test_xmlrpc.FailingServerTestCase.test_fail_no_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github +test.test_xmlrpc.FailingServerTestCase.test_fail_with_info @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_xmlrpc.FaultTestCase.test_dotted_attribute @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_xmlrpc.FaultTestCase.test_dump_fault @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_xmlrpc.FaultTestCase.test_repr @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_zipfile.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_zipfile.txt index 0f05bb5b56..eae188b815 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_zipfile.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_zipfile.txt @@ -3,7 +3,7 @@ test.test_zipfile._path.test_path.TestPath.test_backslash_not_separator @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_zipfile._path.test_path.TestPath.test_dir_parent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_zipfile._path.test_path.TestPath.test_eq_hash @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_zipfile._path.test_path.TestPath.test_extract_orig_with_implied_dirs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64-github +test.test_zipfile._path.test_path.TestPath.test_extract_orig_with_implied_dirs @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_zipfile._path.test_path.TestPath.test_filename @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_zipfile._path.test_path.TestPath.test_getinfo_missing @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_zipfile._path.test_path.TestPath.test_glob_chars @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github @@ -35,7 +35,7 @@ test.test_zipfile._path.test_path.TestPath.test_open_extant_directory @ darwin-a test.test_zipfile._path.test_path.TestPath.test_open_missing_directory @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_zipfile._path.test_path.TestPath.test_open_write @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_zipfile._path.test_path.TestPath.test_parent @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github -test.test_zipfile._path.test_path.TestPath.test_pathlike_construction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64-github +test.test_zipfile._path.test_path.TestPath.test_pathlike_construction @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_zipfile._path.test_path.TestPath.test_pickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github # Times out on Windows in CI !test.test_zipfile._path.test_path.TestPath.test_pickle @ win32-AMD64,win32-AMD64-github 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..d874da1b89 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 @@ -36,10 +36,50 @@ # 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 subprocess import sys +import time import unittest IS_BYTECODE_DSL = sys.implementation.name == 'graalpy' and __graalpython__.is_bytecode_dsl_interpreter +TRANSIENT_GRAALPY_STARTUP_BLOCKING_IO = "ERROR: BlockingIOError: [Errno 11] Resource temporarily unavailable" + + +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 is_native_compression_backend(): + return sys.implementation.name != 'graalpy' or __graalpython__.zlib_module_backend() == 'native' + + +def _jdk_major_version(): + version = __graalpython__.get_jdk_version() + return int(version.split(".", 1)[0].split("-", 1)[0]) + + +def has_capi(): + return not (sys.implementation.name == 'graalpy' and _jdk_major_version() < 25) + + +def needs_capi(test): + if not has_capi(): + return unittest.skip("Needs C API support on JDK 25 or newer")(test) + return test + def skipIfBytecodeDSL(reason=''): def wrapper(test): @@ -75,3 +115,31 @@ def assert_raises(err, fn, *args, err_check=None, **kwargs): else: assert err_check(e) assert raised + + +def _contains_transient_graalpy_startup_blocking_io(output): + if output is None: + return False + if isinstance(output, bytes): + return TRANSIENT_GRAALPY_STARTUP_BLOCKING_IO.encode() in output + return TRANSIENT_GRAALPY_STARTUP_BLOCKING_IO in output + + +def run_subprocess_with_graalpy_startup_retry(args, *, attempts=5, retry_delay=0.2, **kwargs): + unsupported_kwargs = {"stdout", "stderr", "capture_output"} & kwargs.keys() + if unsupported_kwargs: + raise TypeError(f"unsupported keyword arguments: {', '.join(sorted(unsupported_kwargs))}") + check = kwargs.pop("check", False) + for attempt in range(attempts): + result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) + if result.returncode == 0 or not ( + _contains_transient_graalpy_startup_blocking_io(result.stdout) or + _contains_transient_graalpy_startup_blocking_io(result.stderr) + ): + break + if attempt + 1 < attempts: + time.sleep(retry_delay) + retry_delay *= 2 + if check and result.returncode != 0: + raise subprocess.CalledProcessError(result.returncode, result.args, output=result.stdout, stderr=result.stderr) + return result diff --git a/graalpython/com.oracle.graal.python.test/src/tox/leftpad/tox.ini b/graalpython/com.oracle.graal.python.test/src/tox/leftpad/tox.ini index dfc6a98a77..90baa3820c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tox/leftpad/tox.ini +++ b/graalpython/com.oracle.graal.python.test/src/tox/leftpad/tox.ini @@ -3,13 +3,18 @@ envlist = graalpy [testenv] deps = - pytest==6.0.1 + pytest==8.4.2 commands = pytest --assert=plain tests {posargs} [testenv:graalpy] basepython=graalpy -setenv= - PIP_INDEX_URL=https://ol-graal.oraclecorp.com/nexus-cache/repository/pypi-proxy/simple/ - PIP_TRUSTED_HOST=ol-graal.oraclecorp.com -passenv = GRAALPY_LEFTPAD_FAIL,GRAAL_PYTHON_ARGS,GRAAL_PYTHON_VM_ARGS +passenv = + GRAALPY_LEFTPAD_FAIL + GRAAL_PYTHON_ARGS + GRAAL_PYTHON_VM_ARGS + PIP_EXTRA_INDEX_URL + PIP_INDEX_URL + PIP_RETRIES + PIP_TIMEOUT + PIP_TRUSTED_HOST diff --git a/graalpython/com.oracle.graal.python/AGENTS.md b/graalpython/com.oracle.graal.python/AGENTS.md new file mode 100644 index 0000000000..9567d4e747 --- /dev/null +++ b/graalpython/com.oracle.graal.python/AGENTS.md @@ -0,0 +1,32 @@ +# com.oracle.graal.python/ — CORE JAVA IMPLEMENTATION + +## OVERVIEW +Truffle-based Python runtime: AST nodes, builtins, compiler/bytecode support, interop, and shared runtime services. + +## STRUCTURE +```text +com.oracle.graal.python/ +└── src/com/oracle/graal/python/ + ├── runtime/ # Context, POSIX emulation, state, interop runtime glue + ├── nodes/ # Truffle AST nodes + bytecode nodes + ├── builtins/ # Builtin modules/types; @CoreFunctions + @Builtin nodes + ├── lib/ # Shared lib nodes/utilities used across builtins/nodes + ├── compiler/ # Bytecode compiler + DSL tooling + └── util/ # Shared helpers/data structures +``` + +## WHERE TO LOOK +| Task | Location | Notes | +|------|----------|-------| +| Add/fix builtin | `.../builtins/**` | Modules/classes via `@CoreFunctions`; ops via `@Builtin` Nodes. | +| Cross-cutting ops | `.../lib/**` | Prefer adding/reusing lib nodes instead of duplicating patterns. | +| Interop behavior | `.../nodes/interop`, `.../runtime/interop` | Foreign call rules + conversions. | +| Bytecode execution | `.../nodes/bytecode/**` | Root nodes and bytecode interpreter pieces. | + +## CONVENTIONS +- Code style enforced via pre-commit Eclipse formatter + checkstyle; don’t hand-format Java. +- Keep naming/layout close to CPython where practical (helps cross-referencing). + +## ANTI-PATTERNS +- Don’t edit generated sources under `mxbuild/**` or distribution outputs; edit `src/**`. +- Avoid re-implementing common helpers in builtins; check `.../lib/**` first. diff --git a/graalpython/com.oracle.graal.python/pom.xml b/graalpython/com.oracle.graal.python/pom.xml new file mode 100644 index 0000000000..fd0f6790ba --- /dev/null +++ b/graalpython/com.oracle.graal.python/pom.xml @@ -0,0 +1,120 @@ + + + + 4.0.0 + + + org.graalvm.python.ide + graalpy-source-workspace + 1.0-SNAPSHOT + ../../pom.xml + + + com.oracle.graal.python + jar + + + + ${project.groupId} + com.oracle.graal.python.annotations + + + ${project.groupId} + com.oracle.graal.python.pegparser + + + org.graalvm.truffle + truffle-api + + + org.graalvm.tools + profiler-tool + + + org.graalvm.polyglot + polyglot + + + org.graalvm.shadowed + xz + + + org.graalvm.shadowed + icu4j + + + org.graalvm.regex + regex + + + ${project.groupId} + com.oracle.graal.python.processor + provided + true + + + org.graalvm.truffle + truffle-dsl-processor + provided + true + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + full + ${project.build.directory}/generated-sources/annotations + + -h + ${project.build.directory}/jni_gen + + + + + + diff --git a/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/native-image.properties b/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/native-image.properties index 5a301fcffd..a06ea7e9d1 100644 --- a/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/native-image.properties +++ b/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/native-image.properties @@ -1,6 +1,6 @@ # This file contains native-image arguments needed to build graalpython Args = -H:MaxRuntimeCompileMethods=20000 \ --initialize-at-build-time=com.oracle.graal.python,com.oracle.truffle.regex \ - --features=com.oracle.graal.python.BouncyCastleFeature \ --add-exports=org.graalvm.nativeimage/org.graalvm.nativeimage.impl=org.graalvm.py \ - --add-exports=org.graalvm.nativeimage/org.graalvm.nativeimage.impl=ALL-UNNAMED + --add-exports=org.graalvm.nativeimage/org.graalvm.nativeimage.impl=ALL-UNNAMED \ + --features=com.oracle.graal.python.runtime.PythonCryptoFeature diff --git a/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/reflect-config.json b/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/reflect-config.json index 7ec433cff3..83f2445116 100644 --- a/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/reflect-config.json +++ b/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/reflect-config.json @@ -66,2200 +66,151 @@ ] }, { - "name": "com.sun.crypto.provider.AESCipher$General", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.AESKeyGenerator", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.ARCFOURCipher", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.DESCipher", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.DESedeCipher", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.DHParameters", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.GaloisCounterMode$AESGCM", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.HmacCore$HmacSHA384", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.TlsKeyMaterialGenerator", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.TlsMasterSecretGenerator", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "com.sun.crypto.provider.TlsPrfGenerator$V12", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.KeyStoreSpi", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.cert.PKIXRevocationChecker", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.interfaces.DSAPrivateKey", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.interfaces.DSAPublicKey", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.interfaces.ECPrivateKey", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.interfaces.ECPublicKey", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.interfaces.RSAPrivateKey", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.interfaces.RSAPublicKey", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "java.security.spec.DSAParameterSpec", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "javax.crypto.spec.GCMParameterSpec", - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.COMPOSITE$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.CompositeSignatures$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.DH$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.DSA$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.DSTU4145$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.Dilithium$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.EC$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.ECGOST$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.EdEC$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.ElGamal$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.Falcon$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.GM$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.GOST$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.IES$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.LMS$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.NTRU$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.SPHINCSPlus$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.X509$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi$ECDSA", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.ec.SignatureSpi$ecDSA256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Blake2b160", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Blake2b256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Blake2b384", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Blake2b512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2b$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Blake2s128", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Blake2s160", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Blake2s224", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Blake2s256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake2s$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake3$Blake3_256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Blake3$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.DSTU7564$Digest256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.DSTU7564$Digest384", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.DSTU7564$Digest512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.DSTU7564$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.GOST3411$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.GOST3411$Digest2012_256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.GOST3411$Digest2012_512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.GOST3411$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Haraka$Digest256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Haraka$Digest512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Haraka$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest224", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest288", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest384", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Digest512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Keccak$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.MD2$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.MD4$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.MD4$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.MD5$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD128$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD128$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD160$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD160$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD256$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD256$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD320$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.RIPEMD320$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA1$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA224$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA256$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestParallelHash128_256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestParallelHash256_512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestShake128_256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestShake256_512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestTupleHash128_256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA3$DigestTupleHash256_512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA3$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA384$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SHA512$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SM3$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.SM3$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_1024_1024", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_1024_384", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_1024_512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_256_128", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_256_160", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_256_224", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_256_256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_128", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_160", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_224", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_384", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Digest_512_512", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Skein$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Tiger$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Tiger$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Whirlpool$Digest", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.digest.Whirlpool$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.drbg.DRBG$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.keystore.BC$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.keystore.BCFKS$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.keystore.PKCS12$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.AES$AlgParams", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.AES$ECB", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.AES$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.ARC4$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.ARIA$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Blowfish$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.CAST5$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.CAST6$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Camellia$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.ChaCha$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.DES$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.DESede$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.DSTU7624$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.GOST28147$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.GOST3412_2015$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Grain128$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Grainv1$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.HC128$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.HC256$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.IDEA$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Noekeon$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.OpenSSLPBKDF$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF1$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.PBEPBKDF2$PBKDF2withSHA256", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.PBEPKCS12$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Poly1305$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.RC2$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.RC5$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.RC6$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Rijndael$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.SCRYPT$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.SEED$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.SM4$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Salsa20$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Serpent$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Shacal2$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.SipHash$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.SipHash128$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Skipjack$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.TEA$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.TLSKDF$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } - }, - { - "name": "org.bouncycastle.jcajce.provider.symmetric.Twofish$Mappings", + "name": "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.jcajce.provider.symmetric.VMPC$Mappings", + "name": "com.sun.crypto.provider.AESCipher$General", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.jcajce.provider.symmetric.VMPCKSA3$Mappings", + "name": "com.sun.crypto.provider.AESKeyGenerator", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.jcajce.provider.symmetric.XSalsa20$Mappings", + "name": "com.sun.crypto.provider.ARCFOURCipher", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.jcajce.provider.symmetric.XTEA$Mappings", + "name": "com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.jcajce.provider.symmetric.Zuc$Mappings", + "name": "com.sun.crypto.provider.DESCipher", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.pqc.jcajce.provider.BIKE$Mappings", + "name": "com.sun.crypto.provider.DESedeCipher", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.pqc.jcajce.provider.CMCE$Mappings", + "name": "com.sun.crypto.provider.DHParameters", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.pqc.jcajce.provider.Dilithium$Mappings", + "name": "com.sun.crypto.provider.GaloisCounterMode$AESGCM", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.pqc.jcajce.provider.Falcon$Mappings", + "name": "com.sun.crypto.provider.HmacCore$HmacSHA384", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.pqc.jcajce.provider.Frodo$Mappings", + "name": "com.sun.crypto.provider.TlsKeyMaterialGenerator", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.pqc.jcajce.provider.HQC$Mappings", + "name": "com.sun.crypto.provider.TlsMasterSecretGenerator", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.pqc.jcajce.provider.Kyber$Mappings", + "name": "com.sun.crypto.provider.TlsPrfGenerator$V12", "methods": [ { "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { - "name": "org.bouncycastle.pqc.jcajce.provider.LMS$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.KeyStoreSpi" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.NH$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.cert.PKIXRevocationChecker" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.NTRU$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.interfaces.DSAPrivateKey" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.NTRUPrime$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.interfaces.DSAPublicKey" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.Picnic$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.interfaces.ECPrivateKey" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.Rainbow$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.interfaces.ECPublicKey" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.SABER$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.interfaces.RSAPrivateKey" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.SPHINCS$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.interfaces.RSAPublicKey" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.SPHINCSPlus$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "java.security.spec.DSAParameterSpec" }, { - "name": "org.bouncycastle.pqc.jcajce.provider.XMSS$Mappings", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + "name": "javax.crypto.spec.GCMParameterSpec" }, { "name": "sun.security.pkcs12.PKCS12KeyStore", @@ -2268,10 +219,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", @@ -2280,10 +228,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.DSA$SHA1withDSA", @@ -2292,10 +237,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.DSA$SHA224withDSA", @@ -2304,10 +246,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.DSA$SHA256withDSA", @@ -2316,10 +255,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.DSAKeyFactory", @@ -2328,10 +264,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.DSAParameters", @@ -2340,10 +273,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.JavaKeyStore$DualFormatJKS", @@ -2352,10 +282,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.JavaKeyStore$JKS", @@ -2364,10 +291,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.MD2", @@ -2376,10 +300,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.MD5", @@ -2388,10 +309,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.NativePRNG", @@ -2402,10 +320,7 @@ "java.security.SecureRandomParameters" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.NativePRNG$NonBlocking", @@ -2416,10 +331,7 @@ "java.security.SecureRandomParameters" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA", @@ -2428,10 +340,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA2$SHA224", @@ -2440,10 +349,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA2$SHA256", @@ -2452,10 +358,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA3$SHA224", @@ -2464,10 +367,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA3$SHA256", @@ -2476,10 +376,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA3$SHA384", @@ -2488,10 +385,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA3$SHA512", @@ -2500,10 +394,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA5$SHA384", @@ -2512,10 +403,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA5$SHA512", @@ -2524,10 +412,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA5$SHA512_224", @@ -2536,10 +421,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.SHA5$SHA512_256", @@ -2548,10 +430,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.X509Factory", @@ -2560,10 +439,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.certpath.CollectionCertStore", @@ -2574,10 +450,7 @@ "java.security.cert.CertStoreParameters" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.certpath.PKIXCertPathValidator", @@ -2586,10 +459,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.provider.certpath.SunCertPathBuilder", @@ -2598,10 +468,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.rsa.PSSParameters", @@ -2610,10 +477,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.rsa.RSAKeyFactory$Legacy", @@ -2622,10 +486,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.rsa.RSAPSSSignature", @@ -2634,10 +495,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.rsa.RSASignature$MD5withRSA", @@ -2646,10 +504,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.rsa.RSASignature$SHA224withRSA", @@ -2658,10 +513,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.rsa.RSASignature$SHA256withRSA", @@ -2670,10 +522,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.ssl.KeyManagerFactoryImpl$SunX509", @@ -2682,10 +531,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.ssl.SSLContextImpl$TLSContext", @@ -2694,10 +540,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", @@ -2706,10 +549,7 @@ "name": "", "parameterTypes": [] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.AuthorityInfoAccessExtension", @@ -2721,10 +561,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.AuthorityKeyIdentifierExtension", @@ -2736,10 +573,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.BasicConstraintsExtension", @@ -2751,10 +585,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.CRLDistributionPointsExtension", @@ -2771,10 +602,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.CRLNumberExtension", @@ -2786,10 +614,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.CertificatePoliciesExtension", @@ -2801,10 +626,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.ExtendedKeyUsageExtension", @@ -2816,10 +638,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.IssuerAlternativeNameExtension", @@ -2836,10 +655,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.KeyUsageExtension", @@ -2851,10 +667,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.NetscapeCertTypeExtension", @@ -2866,10 +679,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.PrivateKeyUsageExtension", @@ -2881,10 +691,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.SubjectAlternativeNameExtension", @@ -2896,10 +703,7 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] }, { "name": "sun.security.x509.SubjectKeyIdentifierExtension", @@ -2911,9 +715,6 @@ "java.lang.Object" ] } - ], - "condition": { - "typeReachable": "org.bouncycastle.jce.provider.BouncyCastleProvider" - } + ] } ] diff --git a/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/resource-config.json b/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/resource-config.json index 57a0e99313..9c7d004fb0 100644 --- a/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/resource-config.json +++ b/graalpython/com.oracle.graal.python/src/META-INF/native-image/org.graalvm.python/python-language/resource-config.json @@ -1,5 +1,10 @@ { - "resources":[ - {"pattern":"org/graalvm/shadowed/org/jline.*"} + "resources": [ + { + "pattern": "org/graalvm/shadowed/org/jline.*" + }, + { + "pattern": "com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties" + } ] } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/BouncyCastleFeature.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/BouncyCastleFeature.java deleted file mode 100644 index a076b1625d..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/BouncyCastleFeature.java +++ /dev/null @@ -1,108 +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; - -import java.security.Security; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.hosted.RuntimeReflection; -import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; - -import com.oracle.graal.python.builtins.objects.ssl.LazyBouncyCastleProvider; -import com.oracle.graal.python.runtime.PythonImageBuildOptions; - -public class BouncyCastleFeature implements Feature { - - /* - * Will soon be default in native image. We'll still need the old way to support older - * native-image in JDK 21. I guess then it would be something like: - * - * INITIALIZE_AT_RUNTIME = Runtime.version().feature() >= 26; - */ - private static final boolean INITIALIZE_AT_RUNTIME = false; - - @Override - public void afterRegistration(AfterRegistrationAccess access) { - if (!PythonImageBuildOptions.WITHOUT_SSL) { - RuntimeClassInitializationSupport support = ImageSingletons.lookup(RuntimeClassInitializationSupport.class); - - if (INITIALIZE_AT_RUNTIME) { - // Verify at build time, but reinitialize at runtime - support.initializeAtRunTime("org.bouncycastle", "security provider"); - Security.addProvider(new BouncyCastleProvider()); - } else { - support.initializeAtBuildTime("org.bouncycastle", "security provider"); - support.initializeAtRunTime("org.bouncycastle.jcajce.provider.drbg.DRBG$Default", "RNG"); - support.initializeAtRunTime("org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV", "RNG"); - LazyBouncyCastleProvider.initProvider(); - } - - // SSLBasicKeyDerivation looks up the classes below reflectively since jdk-25+23 - // See https://github.com/openjdk/jdk/pull/24393 - String[] reflectiveClasses = new String[]{ - "com.sun.crypto.provider.HKDFKeyDerivation$HKDFSHA256", - "com.sun.crypto.provider.HKDFKeyDerivation$HKDFSHA384", - "com.sun.crypto.provider.HKDFKeyDerivation$HKDFSHA512", - "sun.security.pkcs11.P11HKDF", - }; - for (String name : reflectiveClasses) { - try { - Class.forName(name); - } catch (SecurityException | ClassNotFoundException e) { - return; - } - } - // For backwards compatibility with older JDKs, we only do this if we found - // all those classes - Security.addProvider(Security.getProvider("SunJCE")); - for (String name : reflectiveClasses) { - try { - RuntimeReflection.register(Class.forName(name)); - RuntimeReflection.register(Class.forName(name).getConstructors()); - } catch (SecurityException | ClassNotFoundException e) { - throw new RuntimeException("Could not register " + name + " for reflective access!", e); - } - } - } - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonFileDetector.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonFileDetector.java index cfff664162..c9755764a6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonFileDetector.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonFileDetector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2022, 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 @@ -103,11 +103,11 @@ private static Charset tryGetCharsetFromLine(String line, boolean hasBOM) { if (hasBOM && !normalizedEncoding.equalsUncached(T_UTF_UNDERSCORE_8, TS_ENCODING)) { throw new InvalidEncodingException(encoding + " with BOM"); } - Charset charset = CharsetMapping.getCharsetNormalized(normalizedEncoding); + CharsetMapping.CharsetWrapper charset = CharsetMapping.getCharsetNormalized(normalizedEncoding); if (charset == null) { throw new InvalidEncodingException(encoding); } - return charset; + return charset.charset(); } return null; } 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 664704efba..9e1ba1e502 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 @@ -27,7 +27,6 @@ import static com.oracle.graal.python.annotations.PythonOS.PLATFORM_WIN32; import static com.oracle.graal.python.nodes.BuiltinNames.T__SIGNAL; -import static com.oracle.graal.python.nodes.StringLiterals.J_PY_EXTENSION; import static com.oracle.graal.python.nodes.StringLiterals.T_PY_EXTENSION; import static com.oracle.graal.python.nodes.truffle.TruffleStringMigrationHelpers.isJavaString; import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR; @@ -39,11 +38,9 @@ import java.lang.invoke.VarHandle; import java.nio.charset.StandardCharsets; import java.nio.file.InvalidPathException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.WeakHashMap; @@ -51,18 +48,21 @@ import java.util.concurrent.Semaphore; import java.util.logging.Level; +import org.graalvm.collections.EconomicMap; import org.graalvm.home.Version; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.options.OptionDescriptors; import org.graalvm.options.OptionKey; import org.graalvm.options.OptionValues; import org.graalvm.polyglot.SandboxPolicy; +import org.graalvm.shadowed.com.ibm.icu.text.CaseMap; import com.oracle.graal.python.annotations.PythonOS; import com.oracle.graal.python.builtins.Python3Core; 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; @@ -108,10 +108,10 @@ import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; import com.oracle.graal.python.runtime.PythonImageBuildOptions; import com.oracle.graal.python.runtime.PythonOptions; +import com.oracle.graal.python.runtime.PythonSourceOptions; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.Function; -import com.oracle.graal.python.util.LazySource; import com.oracle.graal.python.util.PythonUtils; import com.oracle.graal.python.util.Supplier; import com.oracle.truffle.api.Assumption; @@ -155,14 +155,8 @@ sandbox = SandboxPolicy.UNTRUSTED, // implementationName = PythonLanguage.IMPLEMENTATION_NAME, // version = PythonLanguage.VERSION, // - characterMimeTypes = {PythonLanguage.MIME_TYPE, - "text/x-python-\0\u0000-eval", "text/x-python-\0\u0000-compile", "text/x-python-\1\u0000-eval", "text/x-python-\1\u0000-compile", "text/x-python-\2\u0000-eval", - "text/x-python-\2\u0000-compile", "text/x-python-\0\u0100-eval", "text/x-python-\0\u0100-compile", "text/x-python-\1\u0100-eval", "text/x-python-\1\u0100-compile", - "text/x-python-\2\u0100-eval", "text/x-python-\2\u0100-compile", "text/x-python-\0\u0040-eval", "text/x-python-\0\u0040-compile", "text/x-python-\1\u0040-eval", - "text/x-python-\1\u0040-compile", "text/x-python-\2\u0040-eval", "text/x-python-\2\u0040-compile", "text/x-python-\0\u0140-eval", "text/x-python-\0\u0140-compile", - "text/x-python-\1\u0140-eval", "text/x-python-\1\u0140-compile", "text/x-python-\2\u0140-eval", "text/x-python-\2\u0140-compile"}, // + characterMimeTypes = {PythonLanguage.MIME_TYPE}, // defaultMimeType = PythonLanguage.MIME_TYPE, // - dependentLanguages = {"nfi", "llvm"}, // interactive = true, internal = false, // contextPolicy = TruffleLanguage.ContextPolicy.SHARED, // fileTypeDetectors = PythonFileDetector.class, // @@ -205,7 +199,10 @@ public final class PythonLanguage extends TruffleLanguage { public static final int GRAALVM_MAJOR; public static final int GRAALVM_MINOR; public static final int GRAALVM_MICRO; - public static final String DEV_TAG; + + /** See {@code mx_graalpython.py:abi_version} */ + public static final String GRAALPY_ABI_VERSION; + public static final String GRAALPY_ABIFLAGS; /* Magic number used to mark pyc files */ public static final int MAGIC_NUMBER = 21000 + Compiler.BYTECODE_VERSION * 10; @@ -226,8 +223,6 @@ public final class PythonLanguage extends TruffleLanguage { static { // The resource file is built by mx from "graalpy-versions" project using mx substitutions. - // The actual values of the versions are computed by mx helper functions py_version_short, - // graal_version_short, and dev_tag defined in mx_graalpython.py try (InputStream is = PythonLanguage.class.getResourceAsStream("/graalpy_versions")) { int ch; if (MAJOR != (ch = is.read() - VERSION_BASE)) { @@ -257,13 +252,9 @@ public final class PythonLanguage extends TruffleLanguage { default: RELEASE_LEVEL_STRING = tsLiteral("final"); } - // see mx.graalpython/mx_graalpython.py:dev_tag - byte[] rev = new byte[3 /* 'dev' */ + 10 /* revision */]; - if (is.read(rev) == rev.length) { - DEV_TAG = new String(rev, StandardCharsets.US_ASCII).strip(); - } else { - DEV_TAG = ""; - } + String[] abiParts = new String(is.readAllBytes(), StandardCharsets.US_ASCII).split("\\R", 2); + GRAALPY_ABI_VERSION = abiParts[0].strip(); + GRAALPY_ABIFLAGS = abiParts.length > 1 ? abiParts[1].strip() : ""; } catch (IOException e) { throw new RuntimeException(e); } @@ -280,48 +271,6 @@ public final class PythonLanguage extends TruffleLanguage { public static final String MIME_TYPE = "text/x-python"; - // the syntax for mime types is as follows - // ::= "text/x-python-" "-" kind - // ::= "compile" | "eval" - // ::= "\0" | "\1" | "\2" - // ::= "\u0040" | "\u0100" | "\u0140" | "\u0000" - // where 0100 implies annotations, and 0040 implies barry_as_flufl - static final String MIME_PREFIX = MIME_TYPE + "-"; - static final int OPT_FLAGS_LEN = 2; // 1 char is optlevel, 1 char is flags - static final String MIME_KIND_COMPILE = "compile"; - static final String MIME_KIND_EVAL = "eval"; - // Since flags are greater than the highest unicode codepoint, we shift them into more - // reasonable values in the mime type. 4 hex digits - static final int MIME_FLAG_SHIFTBY = 4 * 4; - // a dash follows after the opt flag pair - static final int MIME_KIND_START = MIME_PREFIX.length() + OPT_FLAGS_LEN + 1; - - private static boolean mimeTypesComplete(ArrayList mimeJavaStrings) { - ArrayList mimeTypes = new ArrayList<>(); - FutureFeature[] all = FutureFeature.values(); - for (int flagset = 0; flagset < (1 << all.length); ++flagset) { - int flags = 0; - for (int i = 0; i < all.length; ++i) { - if ((flagset & (1 << i)) != 0) { - flags |= all[i].flagValue; - } - } - for (int opt = 0; opt <= 2; opt++) { - for (String typ : new String[]{MIME_KIND_EVAL, MIME_KIND_COMPILE}) { - mimeTypes.add(MIME_PREFIX + optFlagsToMime(opt, flags) + "-" + typ); - mimeJavaStrings.add(String.format("\"%s\\%d\\u%04x-%s\"", MIME_PREFIX, opt, flags >> MIME_FLAG_SHIFTBY, typ)); - } - } - } - HashSet currentMimeTypes = new HashSet<>(List.of(PythonLanguage.class.getAnnotation(Registration.class).characterMimeTypes())); - return currentMimeTypes.containsAll(mimeTypes); - } - - static { - ArrayList mimeJavaStrings = new ArrayList<>(); - assert mimeTypesComplete(mimeJavaStrings) : "Expected all of {" + String.join(", ", mimeJavaStrings) + "} in the PythonLanguage characterMimeTypes"; - } - public static final TruffleString[] T_DEFAULT_PYTHON_EXTENSIONS = new TruffleString[]{T_PY_EXTENSION, tsLiteral(".pyc")}; public static final TruffleLogger LOGGER = TruffleLogger.getLogger(ID, PythonLanguage.class); @@ -367,14 +316,18 @@ public boolean isSingleContext() { public final Assumption noInteropTypeRegisteredAssumption = Truffle.getRuntime().createAssumption("No class for interop registered"); /** - * A thread-safe map to retrieve (and cache) singleton instances of call targets, e.g., for - * Arithmetic operations, wrappers, named cext functions, etc. This reduces the number of call - * targets and allows AST sharing across contexts. The key in this map is either a single value - * or a list of values. + * Root nodes that are populated during native-image build time and then only read at image + * runtime. Using {@link EconomicMap} avoids embedding a large number of + * {@link java.util.concurrent.ConcurrentHashMap.Node} objects into the image heap. */ - private final ConcurrentHashMap cachedCallTargets = new ConcurrentHashMap<>(); + private final EconomicMap imageBuildtimeCachedRootNodes = ImageInfo.inImageCode() ? EconomicMap.create() : null; + /** + * Root nodes added after image startup, or all cached root nodes when running on the JVM. + */ + private final ConcurrentHashMap runtimeCachedRootNodes = new ConcurrentHashMap<>(); - @CompilationFinal(dimensions = 1) private final RootCallTarget[] builtinSlotsCallTargets; + @CompilationFinal(dimensions = 1) private RootCallTarget[] capiCallTargets; + @CompilationFinal(dimensions = 1) private final RootNode[] builtinSlotsRootNodes; /** * We cannot initialize call targets in language ctor and the next suitable hook is context @@ -386,7 +339,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}; /** @@ -417,10 +370,16 @@ public boolean isSingleContext() { /** * A generic source cache for all kinds of {@link Source} objects. For example, this should be - * used to cache the sources created from NFI signature strings to ensure code sharing. + * used to cache synthetic sources to ensure code sharing. */ private final ConcurrentHashMap sourceCache = new ConcurrentHashMap<>(); + /* + * A map from sources without content to either a Source object with content or a TruffleFile + * that can be used to construct such object. + */ + private final WeakHashMap originalSources = new WeakHashMap<>(); + @Idempotent public static PythonLanguage get(Node node) { return REFERENCE.get(node); @@ -431,7 +390,7 @@ public PythonLanguage() { if (PythonBuiltinClassType.PythonClass.getSlots() == null) { throw new IllegalStateException("Slots must be initialized in PythonBuiltinClassType static initializer"); } - builtinSlotsCallTargets = new RootCallTarget[TpSlot.getBuiltinsCallTargetsCount()]; + builtinSlotsRootNodes = new RootNode[TpSlot.getBuiltinsCallTargetsCount()]; } /** @@ -521,6 +480,11 @@ protected OptionDescriptors getOptionDescriptors() { return PythonOptions.DESCRIPTORS; } + @Override + protected OptionDescriptors getSourceOptionDescriptors() { + return PythonSourceOptions.DESCRIPTORS; + } + @Override protected void initializeContext(PythonContext context) { if (!isLanguageInitialized) { @@ -545,114 +509,73 @@ private synchronized void initializeLanguage() { } } - private static String optFlagsToMime(int optimize, int flags) { - if (optimize < 0) { - optimize = 0; - } else if (optimize > 2) { - optimize = 2; - } - String optField = new String(new byte[]{(byte) optimize}); - String flagField = new String(new int[]{(flags & FutureFeature.ALL_FLAGS) >> MIME_FLAG_SHIFTBY}, 0, 1); - assert flagField.length() == 1 : "flags in mime type ended up a surrogate"; - return optField + flagField; - } - - public static String getCompileMimeType(int optimize, int flags) { - String optFlags = optFlagsToMime(optimize, flags); - return MIME_PREFIX + optFlags + "-compile"; - } - - public static String getEvalMimeType(int optimize, int flags) { - String optFlags = optFlagsToMime(optimize, flags); - return MIME_PREFIX + optFlags + "-eval"; + public static SourceBuilder setPythonOptions(SourceBuilder sourceBuilder, InputType kind, int optimize, int flags) { + String sourceKind = switch (kind) { + case FILE -> "file"; + case EVAL -> "eval"; + case SINGLE -> "single"; + default -> throw CompilerDirectives.shouldNotReachHere("unsupported source kind: " + kind); + }; + return sourceBuilder.mimeType(PythonLanguage.MIME_TYPE) // + .option("python.Optimize", Integer.toString(optimize)) // + .option("python.Flags", Integer.toString(flags & FutureFeature.ALL_FLAGS)) // + .option("python.Kind", sourceKind); } @Override protected CallTarget parse(ParsingRequest request) { PythonContext context = PythonContext.get(null); Source source = request.getSource(); - if (source.getMimeType() == null || MIME_TYPE.equals(source.getMimeType())) { - if (!request.getArgumentNames().isEmpty() && source.isInteractive()) { - throw new IllegalStateException("parse with arguments not allowed for interactive sources"); - } - InputType inputType = source.isInteractive() ? InputType.SINGLE : InputType.FILE; - return parse(context, source, inputType, true, 0, source.isInteractive(), request.getArgumentNames(), EnumSet.noneOf(FutureFeature.class)); - } - if (!request.getArgumentNames().isEmpty()) { - throw new IllegalStateException("parse with arguments is only allowed for " + MIME_TYPE + " mime type"); - } - - String mime = source.getMimeType(); - String prefix = mime.substring(0, MIME_PREFIX.length()); - if (!prefix.equals(MIME_PREFIX)) { - throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + mime); + if (!request.getArgumentNames().isEmpty() && source.isInteractive()) { + throw new IllegalStateException("parse with arguments not allowed for interactive sources"); } - String kind = mime.substring(MIME_KIND_START); - InputType type; - if (kind.equals(MIME_KIND_COMPILE)) { - type = InputType.FILE; - } else if (kind.equals(MIME_KIND_EVAL)) { - type = InputType.EVAL; + InputType inputType; + int optimize; + EnumSet futureFeatures; + boolean topLevel; + List argumentNames; + boolean interactiveTerminal; + if (source.isInteractive()) { + inputType = InputType.SINGLE; + optimize = 0; + futureFeatures = EnumSet.noneOf(FutureFeature.class); + topLevel = true; + argumentNames = request.getArgumentNames(); + interactiveTerminal = true; } else { - throw CompilerDirectives.shouldNotReachHere("unknown compilation kind: " + kind + " from mime type: " + mime); - } - int optimize = mime.codePointAt(MIME_PREFIX.length()); - int flags = mime.codePointAt(MIME_PREFIX.length() + 1) << MIME_FLAG_SHIFTBY; - if (0 > optimize || optimize > 2 || (flags & ~FutureFeature.ALL_FLAGS) != 0) { - throw CompilerDirectives.shouldNotReachHere("Invalid value for optlevel or flags: " + optimize + "," + flags + " from mime type: " + mime); - } - assert !source.isInteractive(); - return parse(context, source, type, false, optimize, false, null, FutureFeature.fromFlags(flags)); - } - - public static RootCallTarget callTargetFromBytecode(PythonContext context, Source source, CodeUnit code) { - boolean internal = shouldMarkSourceInternal(context); - SourceBuilder builder = null; - // The original file path should be passed as the name - String name = source.getName(); - if (name != null && !name.isEmpty()) { - builder = sourceForOriginalFile(context, code, internal, name); - if (builder == null) { - if (name.startsWith(FROZEN_FILENAME_PREFIX) && name.endsWith(FROZEN_FILENAME_SUFFIX)) { - String id = name.substring(FROZEN_FILENAME_PREFIX.length(), name.length() - FROZEN_FILENAME_SUFFIX.length()); - String fs = context.getEnv().getFileNameSeparator(); - String path = context.getStdlibHome() + fs + id.replace(".", fs) + J_PY_EXTENSION; - builder = sourceForOriginalFile(context, code, internal, path); - if (builder == null) { - path = context.getStdlibHome() + fs + id.replace(".", fs) + fs + "__init__.py"; - builder = sourceForOriginalFile(context, code, internal, path); - } - } - } + var sourceOptions = source.getOptions(this); + String kind = sourceOptions.get(PythonSourceOptions.Kind); + topLevel = kind.isEmpty(); + inputType = switch (kind) { + case "", "file" -> InputType.FILE; + case "eval" -> InputType.EVAL; + case "single" -> InputType.SINGLE; + default -> throw CompilerDirectives.shouldNotReachHere("unknown compilation kind: " + kind); + }; + optimize = sourceOptions.get(PythonSourceOptions.Optimize); + int flags = sourceOptions.get(PythonSourceOptions.Flags); + futureFeatures = FutureFeature.fromFlags(flags); + argumentNames = request.getArgumentNames().isEmpty() ? null : request.getArgumentNames(); + interactiveTerminal = false; } - if (builder == null) { - builder = Source.newBuilder(source).internal(internal).content(Source.CONTENT_NONE); - } - RootNode rootNode; - LazySource lazySource = new LazySource(builder); + return parse(context, source, inputType, topLevel, optimize, interactiveTerminal, argumentNames, futureFeatures); + } + public RootCallTarget callTargetFromBytecode(Source source, CodeUnit code) { + return callTargetFromBytecode(source, code, source.isInternal()); + } + + public RootCallTarget callTargetFromBytecode(Source source, CodeUnit code, boolean isInternal) { + RootNode rootNode; if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { - // TODO lazily load source in bytecode DSL interpreter too - rootNode = ((BytecodeDSLCodeUnit) code).createRootNode(context, lazySource.getSource()); + rootNode = ((BytecodeDSLCodeUnit) code).createRootNode(this, isInternal); } else { - rootNode = PBytecodeRootNode.create(context.getLanguage(), (BytecodeCodeUnit) code, lazySource, internal); + rootNode = PBytecodeRootNode.create(this, (BytecodeCodeUnit) code, source, isInternal); } return PythonUtils.getOrCreateCallTarget(rootNode); } - private static SourceBuilder sourceForOriginalFile(PythonContext context, CodeUnit code, boolean internal, String path) { - try { - TruffleFile file = context.getEnv().getPublicTruffleFile(path); - if (!file.isReadable()) { - return null; - } - return Source.newBuilder(PythonLanguage.ID, file).name(code.name.toJavaStringUncached()).internal(internal); - } catch (SecurityException | UnsupportedOperationException | InvalidPathException e) { - return null; - } - } - public RootCallTarget parse(PythonContext context, Source source, InputType type, boolean topLevel, int optimize, boolean interactiveTerminal, List argumentNames, EnumSet futureFeatures) { return parse(context, source, type, topLevel, optimize, interactiveTerminal, false, argumentNames, futureFeatures); @@ -734,7 +657,7 @@ private RootNode compileForBytecodeInterpreter(ModTy mod, Source source, int opt Compiler compiler = new Compiler(parserCallbacks); CompilationUnit cu = compiler.compile(mod, EnumSet.noneOf(Compiler.Flags.class), optimize, futureFeatures); BytecodeCodeUnit co = cu.assemble(); - return PBytecodeRootNode.create(this, co, new LazySource(source), source.isInternal(), parserCallbacks); + return PBytecodeRootNode.create(this, co, source, source.isInternal(), parserCallbacks); } private RootNode compileForBytecodeDSLInterpreter(ModTy mod, Source source, int optimize, @@ -798,6 +721,13 @@ public String getName() { } } + public static RootNode unwrapRootNode(RootNode rootNode) { + if (rootNode instanceof RootNodeWithArguments rootNodeWithArguments) { + return rootNodeWithArguments.innerRootNode; + } + return rootNode; + } + @Override public ExecutableNode parse(InlineParsingRequest request) { PythonContext context = PythonContext.get(null); @@ -814,6 +744,7 @@ public Object execute(VirtualFrame frame) { PFrame pFrame = materializeFrameNode.execute(this, false, true, frame); Object pLocals = getFrameLocalsNode.executeCached(frame, pFrame, true); PArguments.setSpecialArgument(arguments, pLocals); + PArguments.setCodeObject(arguments, PFactory.createCode(getLanguage(PythonLanguage.class), callTarget)); PArguments.setGlobals(arguments, PArguments.getGlobals(frame)); boolean wasAcquired = gilNode.acquire(); try { @@ -958,7 +889,7 @@ public static TruffleLogger getCompatibilityLogger(Class clazz) { return TruffleLogger.getLogger(ID, "compatibility." + clazz.getName()); } - public static Source newSource(PythonContext ctxt, TruffleString tsrc, TruffleString name, boolean mayBeFile, String mime) { + public static Source newSource(PythonContext ctxt, TruffleString tsrc, TruffleString name, boolean mayBeFile, InputType inputType, int optimize, int flags) { try { SourceBuilder sourceBuilder = null; String src = tsrc.toJavaStringUncached(); @@ -983,9 +914,7 @@ public static Source newSource(PythonContext ctxt, TruffleString tsrc, TruffleSt if (sourceBuilder == null) { sourceBuilder = Source.newBuilder(ID, src, name.toJavaStringUncached()); } - if (mime != null) { - sourceBuilder.mimeType(mime); - } + sourceBuilder = PythonLanguage.setPythonOptions(sourceBuilder, inputType, optimize, flags); return newSource(ctxt, sourceBuilder); } catch (IOException e) { throw new IllegalStateException(e); @@ -1008,7 +937,7 @@ private static Source newSource(PythonContext context, SourceBuilder srcBuilder) return srcBuilder.build(); } - private static boolean shouldMarkSourceInternal(PythonContext ctxt) { + public static boolean shouldMarkSourceInternal(PythonContext ctxt) { return !ctxt.isCoreInitialized() && !ctxt.getLanguage().getEngineOption(PythonOptions.ExposeInternalSources); } @@ -1061,6 +990,7 @@ public long cacheKeyForBytecode(byte[] code) { @CompilationFinal private Object cachedTRegexLineBreakRegex; public Object getCachedTRegexLineBreakRegex(Node location, PythonContext context) { + CompilerAsserts.partialEvaluationConstant(this); if (cachedTRegexLineBreakRegex == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); cachedTRegexLineBreakRegex = context.getEnv().parseInternal(LINEBREAK_REGEX_SOURCE).call(location); @@ -1068,6 +998,17 @@ public Object getCachedTRegexLineBreakRegex(Node location, PythonContext context return cachedTRegexLineBreakRegex; } + @CompilationFinal private CaseMap.Title cachedICUTitleCaser; + + public CaseMap.Title getCachedICUTitleCaser() { + CompilerAsserts.partialEvaluationConstant(this); + if (cachedICUTitleCaser == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + cachedICUTitleCaser = CaseMap.toTitle().wholeString().noBreakAdjustment(); + } + return cachedICUTitleCaser; + } + @Override protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) { if (singleThreaded) { @@ -1133,118 +1074,122 @@ private Shape createBuiltinShape(PythonBuiltinClassType type, int ordinal) { } public RootCallTarget getBuiltinSlotCallTarget(int index) { - return builtinSlotsCallTargets[index]; + RootNode rootNode = builtinSlotsRootNodes[index]; + assert rootNode != null : index; + return rootNode.getCallTarget(); } - public void setBuiltinSlotCallTarget(int index, RootCallTarget callTarget) { + public void setBuiltinSlotRootNode(int index, RootNode rootNode) { VarHandle.storeStoreFence(); - builtinSlotsCallTargets[index] = callTarget; + builtinSlotsRootNodes[index] = rootNode; } - /** - * 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 - * parameters that alter their behavior as part of the cache key. - */ - public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Class key) { - // It's complicated with RootNodes, but regular nodes should have only parameterless ctor to - // be appropriate keys for the cache - assert RootNode.class.isAssignableFrom(key) || key.getConstructors().length <= 1; - assert RootNode.class.isAssignableFrom(key) || key.getConstructors().length == 0 || key.getConstructors()[0].getParameterCount() == 0; - return createCachedCallTargetUnsafe(rootNodeFunction, key, true); + public RootCallTarget getCapiCallTarget(int index) { + if (capiCallTargets == null) { + return null; + } + return capiCallTargets[index]; } - public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Enum key) { - return createCachedCallTargetUnsafe(rootNodeFunction, key, true); + 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; } - 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 - // node itself, there must be finite number of strings that can appear here (i.e., must not - // be dynamically generated unless their number is bounded). - return createCachedCallTargetUnsafe(rootNodeFunction, true, nodeClass, key); + public T createCachedRootNode(Function rootNodeFunction, Class key) { + assert RootNode.class.isAssignableFrom(key) || key.getConstructors().length <= 1; + assert RootNode.class.isAssignableFrom(key) || key.getConstructors().length == 0 || key.getConstructors()[0].getParameterCount() == 0; + return createCachedRootNodeUnsafe(rootNodeFunction, key, true); } - // Variant that should be called at most once per context, because it does not cache the target - // in single context mode - public RootCallTarget initBuiltinCallTarget(Function rootNodeFunction, Class nodeClass, String key) { - return createCachedCallTargetUnsafe(rootNodeFunction, false, nodeClass, key); + public RootNode createCachedRootNode(Function rootNodeFunction, Enum key) { + return createCachedRootNodeUnsafe(rootNodeFunction, key, true); } - public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Class nodeClass, TruffleString key) { - // See the String overload - return createCachedCallTargetUnsafe(rootNodeFunction, true, nodeClass, key); + public T createCachedRootNode(Function rootNodeFunction, Class nodeClass, String key) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, nodeClass, key); } - public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Class nodeClass, int key) { - return createCachedCallTargetUnsafe(rootNodeFunction, true, nodeClass, key); + // Variant that should be called at most once per context, because it does not cache the root + // node in single context mode + public T initBuiltinRootNode(Function rootNodeFunction, Class nodeClass, String key) { + return createCachedRootNodeUnsafe(rootNodeFunction, false, nodeClass, key); } - public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Class nodeClass1, Class nodeClass2, String name) { - // for slot call targets and wrappers: the root node may be wrapping a helper wrapper node - // implementing the slot wrapper logic and the bare slot node itself - return createCachedCallTargetUnsafe(rootNodeFunction, true, nodeClass1, nodeClass2, name); + public RootNode createCachedRootNode(Function rootNodeFunction, Class nodeClass, TruffleString key) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, nodeClass, key); } - public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Class nodeClass1, Class nodeClass2, PythonBuiltinClassType type, String name) { - // for slot wrappers: the type is used for validation of "self" type - return createCachedCallTargetUnsafe(rootNodeFunction, true, nodeClass1, nodeClass2, type, name); + public RootNode createCachedRootNode(Function rootNodeFunction, Class nodeClass, int key) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, nodeClass, key); } - /** - * Caches call targets for external C functions created by extensions at runtime. - *

- * For the time being, we assume finite/limited number of extensions and their external - * functions. This may hold onto call targets created by one extension used in a context that - * was closed in the meanwhile and no other context ever loads the extension. - */ - public RootCallTarget createCachedExternalFunWrapperCallTarget(Function rootNodeFunction, - Class klass, Enum signature, TruffleString name, - boolean doArgumentAndResultConversion, boolean isStatic) { - return createCachedCallTargetUnsafe(rootNodeFunction, true, klass, signature, name, doArgumentAndResultConversion, isStatic); + public T createCachedRootNode(Function rootNodeFunction, Class nodeClass1, Class nodeClass2, String name) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, nodeClass1, nodeClass2, name); } - public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Enum signature, TruffleString name, - boolean doArgumentAndResultConversion) { - return createCachedCallTargetUnsafe(rootNodeFunction, true, signature, name, doArgumentAndResultConversion); + public T createCachedRootNode(Function rootNodeFunction, Class nodeClass1, Class nodeClass2, PythonBuiltinClassType type, String name) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, nodeClass1, nodeClass2, type, name); } - public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Enum signature, TruffleString name) { - return createCachedCallTargetUnsafe(rootNodeFunction, true, signature, name); + public T createCachedRootNode(Function rootNodeFunction, CodeUnit key) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, key); } - public RootCallTarget createStructSeqIndexedMemberAccessCachedCallTarget(Function rootNodeFunction, int memberIndex) { - return createCachedCallTargetUnsafe(rootNodeFunction, true, StructSequence.class, memberIndex); + public T createCachedExternalFunWrapperRootNode(Function rootNodeFunction, + Class klass, Enum signature, TruffleString name, + boolean doArgumentAndResultConversion, boolean isStatic) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, klass, signature, name, doArgumentAndResultConversion, isStatic); } - public RootCallTarget createCachedPropAccessCallTarget(Function rootNodeFunction, Class nodeClass, String name, int type, int offset) { - // For the time being, we assume finite/limited number of cext/hpy types members, their - // types and offsets - return createCachedCallTargetUnsafe(rootNodeFunction, true, nodeClass, name, type, offset); + public T createStructSeqIndexedMemberAccessCachedRootNode(Function rootNodeFunction, int memberIndex) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, StructSequence.class, memberIndex); } - /** - * Keys in any caches held by {@link PythonLanguage} must be context independent objects and - * there must be either finite number of their instances, or if the key is a context independent - * mirror of some runtime data structure, it must be cached weakly. This call targets cache is - * strong. - *

- * To avoid memory leaks, all key types must be known to have finite number of possible - * instances. Public methods for adding to the cache must take concrete key type(s) so that all - * possible cache keys are explicit and documented. - */ - private RootCallTarget createCachedCallTargetUnsafe(Function rootNodeFunction, Object key, boolean cacheInSingleContext) { + public T createCachedPropAccessRootNode(Function rootNodeFunction, Class nodeClass, String name, int type, int offset) { + return createCachedRootNodeUnsafe(rootNodeFunction, true, nodeClass, name, type, offset); + } + + private T createCachedRootNodeUnsafe(Function rootNodeFunction, Object key, boolean cacheInSingleContext) { CompilerAsserts.neverPartOfCompilation(); if (cacheInSingleContext || !singleContext) { - return cachedCallTargets.computeIfAbsent(key, k -> PythonUtils.getOrCreateCallTarget(rootNodeFunction.apply(this))); + return getOrCreateCachedRootNode(rootNodeFunction, key); } else { - return PythonUtils.getOrCreateCallTarget(rootNodeFunction.apply(this)); + return rootNodeFunction.apply(this); } } - private RootCallTarget createCachedCallTargetUnsafe(Function rootNodeFunction, boolean cacheInSingleContext, Object... cacheKeys) { - return createCachedCallTargetUnsafe(rootNodeFunction, Arrays.asList(cacheKeys), cacheInSingleContext); + private T createCachedRootNodeUnsafe(Function rootNodeFunction, boolean cacheInSingleContext, Object... cacheKeys) { + return createCachedRootNodeUnsafe(rootNodeFunction, Arrays.asList(cacheKeys), cacheInSingleContext); + } + + @SuppressWarnings("unchecked") + private T getOrCreateCachedRootNode(Function rootNodeFunction, Object key) { + CompilerAsserts.neverPartOfCompilation(); + if (ImageInfo.inImageRuntimeCode()) { + RootNode preinitialized = imageBuildtimeCachedRootNodes.get(key); + if (preinitialized != null) { + return (T) preinitialized; + } + return (T) runtimeCachedRootNodes.computeIfAbsent(key, k -> rootNodeFunction.apply(this)); + } + if (ImageInfo.inImageBuildtimeCode()) { + synchronized (imageBuildtimeCachedRootNodes) { + RootNode cached = imageBuildtimeCachedRootNodes.get(key); + if (cached == null) { + cached = rootNodeFunction.apply(this); + imageBuildtimeCachedRootNodes.put(key, cached); + } + return (T) cached; + } + } + return (T) runtimeCachedRootNodes.computeIfAbsent(key, k -> rootNodeFunction.apply(this)); } @Override @@ -1306,6 +1251,36 @@ public Source getOrCreateSource(Function rootNodeFunction, Objec return sourceCache.computeIfAbsent(key, rootNodeFunction); } + public Source getOrCreateSourceWithContent(Source sourceWithoutContent) { + if (sourceWithoutContent.hasCharacters()) { + return sourceWithoutContent; + } + synchronized (originalSources) { + Object original = originalSources.get(sourceWithoutContent); + if (original instanceof Source originalSource) { + return originalSource; + } + if (original instanceof TruffleFile originalFile) { + Source source; + try { + source = Source.newBuilder(ID, originalFile).name(sourceWithoutContent.getName()).internal(sourceWithoutContent.isInternal()).mimeType(MIME_TYPE).build(); + } catch (IOException | SecurityException | UnsupportedOperationException | IllegalArgumentException e) { + source = sourceWithoutContent; + } + originalSources.put(sourceWithoutContent, source); + return source; + } + assert original == null; + return sourceWithoutContent; + } + } + + public void registerOriginalFile(Source sourceWithoutContent, TruffleFile originalFile) { + synchronized (originalSources) { + originalSources.put(sourceWithoutContent, originalFile); + } + } + public static PythonOS getPythonOS() { if (PythonOS.internalCurrent == PythonOS.PLATFORM_ANY) { if (ImageInfo.inImageBuildtimeCode()) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java index 1e537ec582..17b5fec576 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java @@ -75,6 +75,7 @@ import com.oracle.graal.python.builtins.modules.CodecsModuleBuiltins; import com.oracle.graal.python.builtins.modules.CodecsTruffleModuleBuiltins; import com.oracle.graal.python.builtins.modules.CollectionsModuleBuiltins; +import com.oracle.graal.python.builtins.modules.codecs.CodecsRegistry; import com.oracle.graal.python.builtins.modules.ContextvarsModuleBuiltins; import com.oracle.graal.python.builtins.modules.CryptModuleBuiltins; import com.oracle.graal.python.builtins.modules.ErrnoModuleBuiltins; @@ -93,11 +94,13 @@ import com.oracle.graal.python.builtins.modules.MsvcrtModuleBuiltins; import com.oracle.graal.python.builtins.modules.NtModuleBuiltins; import com.oracle.graal.python.builtins.modules.OperatorModuleBuiltins; +import com.oracle.graal.python.builtins.modules.OverlappedModuleBuiltins; import com.oracle.graal.python.builtins.modules.PolyglotModuleBuiltins; import com.oracle.graal.python.builtins.modules.PosixModuleBuiltins; import com.oracle.graal.python.builtins.modules.PosixShMemModuleBuiltins; import com.oracle.graal.python.builtins.modules.PosixSubprocessModuleBuiltins; import com.oracle.graal.python.builtins.modules.PwdModuleBuiltins; +import com.oracle.graal.python.builtins.modules.pyexpat.PyExpatModuleBuiltins; import com.oracle.graal.python.builtins.modules.QueueModuleBuiltins; import com.oracle.graal.python.builtins.modules.RandomModuleBuiltins; import com.oracle.graal.python.builtins.modules.ReadlineModuleBuiltins; @@ -120,6 +123,7 @@ import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins; import com.oracle.graal.python.builtins.modules.WeakRefModuleBuiltins; import com.oracle.graal.python.builtins.modules.WinapiModuleBuiltins; +import com.oracle.graal.python.builtins.modules.WinregLegacyModuleBuiltins; import com.oracle.graal.python.builtins.modules.WinregModuleBuiltins; import com.oracle.graal.python.builtins.modules.ast.AstBuiltins; import com.oracle.graal.python.builtins.modules.ast.AstModuleBuiltins; @@ -224,6 +228,7 @@ import com.oracle.graal.python.builtins.objects.bytes.BytesCommonBuiltins; import com.oracle.graal.python.builtins.objects.cell.CellBuiltins; import com.oracle.graal.python.builtins.objects.code.CodeBuiltins; +import com.oracle.graal.python.builtins.objects.code.PCode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.complex.ComplexBuiltins; import com.oracle.graal.python.builtins.objects.contextvars.ContextBuiltins; @@ -266,6 +271,7 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeZoneBuiltins; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.function.AbstractFunctionBuiltins; import com.oracle.graal.python.builtins.objects.function.BuiltinFunctionBuiltins; @@ -364,6 +370,7 @@ import com.oracle.graal.python.builtins.objects.tuple.InstantiableStructSequenceBuiltins; import com.oracle.graal.python.builtins.objects.tuple.StructSequenceBuiltins; import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins; +import com.oracle.graal.python.builtins.modules.pyexpat.XMLParserBuiltins; import com.oracle.graal.python.builtins.objects.tuple.TupleGetterBuiltins; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; @@ -413,6 +420,7 @@ import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.strings.TruffleString; +import org.graalvm.nativeimage.ImageInfo; /** * The core is a historical artifact and PythonContext and Python3Core should be merged. @@ -430,18 +438,12 @@ public abstract class Python3Core { private static TruffleString[] getCoreFiles() { // Order matters! - List coreFiles = List.of( + return new TruffleString[]{ T___GRAALPYTHON__, T__SRE, T__SYSCONFIG, - T_JAVA, - toTruffleStringUncached("pip_hook")); - if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_WIN32) { - coreFiles = new ArrayList<>(coreFiles); - coreFiles.add(toTruffleStringUncached("_nt")); - } - - return coreFiles.toArray(new TruffleString[0]); + T_JAVA + }; } private PythonBuiltins[] builtins; @@ -465,6 +467,7 @@ private static void filterBuiltins(List builtins) { toRemove.add(builtin); } else { CoreFunctions annotation = builtin.getClass().getAnnotation(CoreFunctions.class); + builtin.setNeedsPostInitialize(annotation.isEager() || annotation.extendClasses().length != 0); if (annotation.os() != PythonOS.PLATFORM_ANY && annotation.os() != currentOs) { toRemove.add(builtin); } @@ -497,6 +500,7 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new ForeignObjectBuiltins(), new ForeignNumberBuiltins(), new ForeignBooleanBuiltins(), + new ForeignTimeZoneBuiltins(), new ForeignAbstractClassBuiltins(), new ForeignExecutableBuiltins(), new ForeignInstantiableBuiltins(), @@ -554,6 +558,8 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new WinregModuleBuiltins(), new MsvcrtModuleBuiltins(), new WinapiModuleBuiltins(), + new OverlappedModuleBuiltins(), + new WinregLegacyModuleBuiltins(), new CryptModuleBuiltins(), new ScandirIteratorBuiltins(), new DirEntryBuiltins(), @@ -658,8 +664,10 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) { new JavaModuleBuiltins(), new JArrayModuleBuiltins(), new CSVModuleBuiltins(), + new PyExpatModuleBuiltins(), new JSONModuleBuiltins(), new SREModuleBuiltins(), + new XMLParserBuiltins(), new AstModuleBuiltins(), PythonImageBuildOptions.WITHOUT_NATIVE_POSIX && (PythonImageBuildOptions.WITHOUT_JAVA_INET || !env.isSocketIOAllowed()) ? null : new SelectModuleBuiltins(), PythonImageBuildOptions.WITHOUT_NATIVE_POSIX && (PythonImageBuildOptions.WITHOUT_JAVA_INET || !env.isSocketIOAllowed()) ? null : new SocketModuleBuiltins(), @@ -937,6 +945,9 @@ public final void initialize(PythonContext context) { this.builtins = initializeBuiltins(context.getEnv()); initializeJavaCore(); initializeImportlib(); + if (context.getEnv().isPreInitialization()) { + CodecsRegistry.initialize(context); + } context.applyModuleOptions(); initializePython3Core(context.getCoreHomeOrFail()); initialized = true; @@ -1039,6 +1050,17 @@ private void initializePython3Core(TruffleString coreHome) { initialized = true; } + private void initializeWindowsCoreFiles(TruffleString coreHome) { + if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_WIN32) { + assert !ImageInfo.inImageBuildtimeCode(); + loadFile(toTruffleStringUncached("_nt"), coreHome); + loadFile(toTruffleStringUncached("_winapi"), toTruffleStringUncached("modules/_winapi"), coreHome); + loadFile(toTruffleStringUncached("_overlapped"), toTruffleStringUncached("modules/_overlapped"), coreHome); + loadFile(toTruffleStringUncached("winreg"), toTruffleStringUncached("modules/winreg"), coreHome); + loadFile(toTruffleStringUncached("_winreg"), toTruffleStringUncached("modules/_winreg"), coreHome); + } + } + /** * Run post-initialization code that needs a fully working Python environment. This will be run * eagerly when the context is initialized on the JVM or a new context is created on SVM, but is @@ -1047,19 +1069,19 @@ private void initializePython3Core(TruffleString coreHome) { public final void postInitialize(Env env) { if (!env.isPreInitialization()) { initialized = false; + initializeWindowsCoreFiles(getContext().getCoreHomeOrFail()); for (PythonBuiltins builtin : builtins) { - CoreFunctions annotation = builtin.getClass().getAnnotation(CoreFunctions.class); - if (annotation.isEager() || annotation.extendClasses().length != 0) { + if (builtin.needsPostInitialize()) { builtin.postInitialize(this); } } /* * Special case for _bz2: If native access is not allowed, we cannot use the built-in - * implementation that would call libbz2 via NFI. Therefore, we remove it from the + * implementation that would call libbz2 via NativeAccess. Therefore, we remove it from the * built-in modules map (and also from sys.modules if already loaded). This will cause a - * fallback to another _bz2 implementation (e.g. LLVM or maybe some Java lib). This + * fallback to another _bz2 implementation (e.g. some Java lib). This * needs to be done here and cannot be done in 'initializeBuiltins' because then we * would never include the intrinsified _bz2 module in the native image since native * access is never allowed during context pre-initialization. @@ -1307,16 +1329,24 @@ private Source getInternalSource(TruffleString basename, TruffleString prefix) { throw e; } - private void loadFile(TruffleString s, TruffleString prefix) { + public void loadFile(TruffleString s, TruffleString prefix) { + loadFile(s, s, prefix); + } + + private void loadFile(TruffleString s, TruffleString sourceName, TruffleString prefix) { PythonModule mod = lookupBuiltinModule(s); if (mod == null) { // use an anonymous module for the side-effects mod = PFactory.createPythonModule(T___ANONYMOUS__); } - loadFile(s, prefix, mod); + loadFile(s, sourceName, prefix, mod); } private void loadFile(TruffleString s, TruffleString prefix, PythonModule mod) { + loadFile(s, s, prefix, mod); + } + + private void loadFile(TruffleString s, TruffleString sourceName, TruffleString prefix, PythonModule mod) { if (ImpModuleBuiltins.importFrozenModuleObjectNoRaise(this, cat(T_GRAALPYTHON, T_DOT, s), mod) != null) { LOGGER.log(Level.FINE, () -> "import '" + s + "' # "); return; @@ -1324,11 +1354,12 @@ private void loadFile(TruffleString s, TruffleString prefix, PythonModule mod) { LOGGER.log(Level.FINE, () -> "import '" + s + "'"); Supplier getCode = () -> { - Source source = getInternalSource(s, prefix); + Source source = getInternalSource(sourceName, prefix); return getLanguage().parse(getContext(), source, InputType.FILE, false, 0, false, null, EnumSet.noneOf(FutureFeature.class)); }; - RootCallTarget callTarget = (RootCallTarget) getLanguage().cacheCode(s, getCode); - CallDispatchers.SimpleIndirectInvokeNode.executeUncached(callTarget, PArguments.withGlobals(mod)); + RootCallTarget callTarget = (RootCallTarget) getLanguage().cacheCode(sourceName, getCode); + PCode code = PFactory.createCode(language, callTarget); + CallDispatchers.SimpleIndirectInvokeNode.executeUncached(callTarget, PArguments.withGlobals(code, mod)); } public final PInt getTrue() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java index 86e67d02c2..d1a36fd72a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java @@ -51,6 +51,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.J_POLYGLOT; import static com.oracle.graal.python.nodes.BuiltinNames.J_POSIX; import static com.oracle.graal.python.nodes.BuiltinNames.J_PROPERTY; +import static com.oracle.graal.python.nodes.BuiltinNames.J_SIGNAL; import static com.oracle.graal.python.nodes.BuiltinNames.J_SHA1; import static com.oracle.graal.python.nodes.BuiltinNames.J_SHA2; import static com.oracle.graal.python.nodes.BuiltinNames.J_SHA3; @@ -63,6 +64,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.J_TYPING; import static com.oracle.graal.python.nodes.BuiltinNames.J_WRAPPER_DESCRIPTOR; import static com.oracle.graal.python.nodes.BuiltinNames.J__CONTEXTVARS; +import static com.oracle.graal.python.nodes.BuiltinNames.J__SIGNAL; import static com.oracle.graal.python.nodes.BuiltinNames.J__SOCKET; import static com.oracle.graal.python.nodes.BuiltinNames.J__SSL; import static com.oracle.graal.python.nodes.BuiltinNames.J__STRUCT; @@ -129,6 +131,7 @@ import com.oracle.graal.python.builtins.modules.pickle.PicklerMemoProxyBuiltins; import com.oracle.graal.python.builtins.modules.pickle.UnpicklerBuiltins; import com.oracle.graal.python.builtins.modules.pickle.UnpicklerMemoProxyBuiltins; +import com.oracle.graal.python.builtins.modules.pyexpat.XMLParserBuiltins; import com.oracle.graal.python.builtins.modules.re.MatchBuiltins; import com.oracle.graal.python.builtins.modules.re.PatternBuiltins; import com.oracle.graal.python.builtins.modules.weakref.ProxyTypeBuiltins; @@ -185,6 +188,7 @@ import com.oracle.graal.python.builtins.objects.foreign.ForeignIterableBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignNumberBuiltins; import com.oracle.graal.python.builtins.objects.foreign.ForeignObjectBuiltins; +import com.oracle.graal.python.builtins.objects.foreign.ForeignTimeZoneBuiltins; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.function.AbstractFunctionBuiltins; import com.oracle.graal.python.builtins.objects.function.FunctionBuiltins; @@ -777,11 +781,13 @@ It can be called either on the class (e.g. C.f()) or on an instance TimeoutError("TimeoutError", OSError, newBuilder().publishInModule(J_BUILTINS).basetype().addDict()), ZLibError("error", Exception, newBuilder().publishInModule("zlib").basetype().addDict()), CSVError("Error", Exception, newBuilder().publishInModule("_csv").basetype().addDict()), + PyExpatError("error", Exception, newBuilder().publishInModule("pyexpat").basetype().addDict()), LZMAError("LZMAError", Exception, newBuilder().publishInModule("_lzma").basetype().addDict()), StructError("StructError", Exception, newBuilder().publishInModule(J__STRUCT).basetype().addDict()), PickleError("PickleError", Exception, newBuilder().publishInModule("_pickle").basetype().addDict()), PicklingError("PicklingError", PickleError, newBuilder().publishInModule("_pickle").basetype().addDict()), UnpicklingError("UnpicklingError", PickleError, newBuilder().publishInModule("_pickle").basetype().addDict()), + SignalItimerError("itimer_error", OSError, newBuilder().publishInModule(J__SIGNAL).moduleName(J_SIGNAL).basetype().addDict()), SocketGAIError("gaierror", OSError, newBuilder().publishInModule(J__SOCKET).basetype().addDict()), SocketHError("herror", OSError, newBuilder().publishInModule(J__SOCKET).basetype().addDict()), BinasciiError("Error", ValueError, newBuilder().publishInModule("binascii").basetype().addDict()), @@ -987,7 +993,13 @@ accepted by asctime(), mktime() and strftime(). May be considered as a UnraisableHookArgs Type used to pass arguments to sys.unraisablehook.""")), + PExceptHookArgs( + "_ExceptHookArgs", + PTuple, + newBuilder().publishInModule(J__THREAD).slots(StructSequenceBuiltins.SLOTS, InstantiableStructSequenceBuiltins.SLOTS).doc(""" + _ExceptHookArgs + Type used to pass arguments to _thread._excepthook.""")), PSSLSession("SSLSession", PythonObject, newBuilder().publishInModule(J__SSL).disallowInstantiation()), PSSLContext("_SSLContext", PythonObject, newBuilder().publishInModule(J__SSL).basetype().slots(SSLContextBuiltins.SLOTS)), PSSLSocket("_SSLSocket", PythonObject, newBuilder().publishInModule(J__SSL).basetype()), @@ -1171,6 +1183,11 @@ def takewhile(predicate, iterable): PythonObject, newBuilder().publishInModule("_json").basetype().slots(JSONEncoderBuiltins.SLOTS).doc(""" _iterencode(obj, _current_indent_level) -> iterable""")), + XMLParser( + "xmlparser", + PythonObject, + newBuilder().publishInModule("pyexpat").basetype().disallowInstantiation().slots(XMLParserBuiltins.SLOTS).doc(""" + pyexpat XML parser object""")), // datetime PDate( @@ -1223,6 +1240,12 @@ def takewhile(predicate, iterable): PTzInfo, newBuilder().moduleName("datetime").publishInModule("_datetime").slots(TimeZoneBuiltins.SLOTS).doc("Fixed offset from UTC implementation of tzinfo.")), + // foreign datetime + ForeignDate("ForeignDate", PDate, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignTime("ForeignTime", PTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignDateTime("ForeignDateTime", PDateTime, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation()), + ForeignTimeZone("ForeignTimeZone", PTzInfo, newBuilder().publishInModule(J_POLYGLOT).basetype().addDict().disallowInstantiation().slots(ForeignTimeZoneBuiltins.SLOTS)), + // re PPattern( "Pattern", @@ -1468,7 +1491,7 @@ def add_two(x: float, y: float) -> float: See PEP 695 for more information. """)), - PGeneric(J_GENERIC, PythonObject, newBuilder().publishInModule(J__TYPING).moduleName(J_TYPING).basetype()); + PGeneric(J_GENERIC, PythonObject, newBuilder().publishInModule(J__TYPING).moduleName(J_TYPING).basetype().heaptype()); private static TypeBuilder newBuilder() { return new TypeBuilder(); @@ -1567,7 +1590,10 @@ public TypeBuilder doc(String doc) { private final TpSlots declaredSlots; /** - * The actual slots including slots inherited from base classes + * The actual slots including slots inherited from base classes. + * + * n.b.: this field is positioned to be at the same offset as the one in + * {@link com.oracle.graal.python.builtins.objects.type.PythonManagedClass#tpSlots}. */ private final TpSlots slots; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java index 2260021d2f..0a5b3c4b47 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.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. @@ -42,6 +42,7 @@ import com.oracle.graal.python.builtins.modules.ImpModuleBuiltins.ExecBuiltin; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; +import com.oracle.graal.python.builtins.objects.function.Signature; 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.PythonBuiltinClass; @@ -49,7 +50,6 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.BiConsumer; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.strings.TruffleString; @@ -61,6 +61,12 @@ public abstract class PythonBuiltins { protected abstract List> getNodeFactories(); private boolean initialized; + /** + * Cached during builtin setup so {@link Python3Core#postInitialize(Env)} does not need to + * read {@link CoreFunctions} annotations at runtime, which shows up as boot-time page faults + * in native startup traces. + */ + private boolean needsPostInitialize; public boolean isInitialized() { return initialized; @@ -70,6 +76,14 @@ public void setInitialized(boolean initialized) { this.initialized = initialized; } + public boolean needsPostInitialize() { + return needsPostInitialize; + } + + public void setNeedsPostInitialize(boolean needsPostInitialize) { + this.needsPostInitialize = needsPostInitialize; + } + /** * Initialize everything that is truly independent of commandline arguments and that can be * initialized and frozen into an SVM image. When in a subclass, any modifications to @@ -94,11 +108,12 @@ public void initialize(Python3Core core) { } TruffleString tsName = toInternedTruffleStringUncached(builtin.name()); PythonLanguage language = core.getLanguage(); - RootCallTarget callTarget = language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, declaresExplicitSelf), factory.getNodeClass(), - builtin.name()); Object builtinDoc = builtin.doc().isEmpty() ? PNone.NONE : toTruffleStringUncached(builtin.doc()); - int flags = PBuiltinFunction.getFlags(builtin, callTarget); - PBuiltinFunction function = PFactory.createBuiltinFunction(language, tsName, null, numDefaults(builtin), flags, callTarget); + BuiltinFunctionRootNode rootNode = language.initBuiltinRootNode(l -> new BuiltinFunctionRootNode(l, builtin, factory, declaresExplicitSelf), + factory.getNodeClass(), builtin.name()); + Signature signature = rootNode.getSignature(); + int flags = PBuiltinFunction.getFlags(builtin, signature); + PBuiltinFunction function = PFactory.createBuiltinFunction(language, tsName, null, numDefaults(builtin), flags, rootNode); function.setAttribute(T___DOC__, builtinDoc); BoundBuiltinCallable callable = function; if (builtin.isGetter() || builtin.isSetter()) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/AtexitModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/AtexitModuleBuiltins.java index 16003763d2..7135fbc3d8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/AtexitModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/AtexitModuleBuiltins.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 @@ -66,7 +66,6 @@ import com.oracle.graal.python.runtime.exception.ExceptionUtils; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NodeFactory; @@ -135,8 +134,8 @@ private static void handleException(PythonContext context, PException e) { @Specialization static Object register(Object callable, Object[] arguments, PKeyword[] keywords) { PythonContext context = PythonContext.get(null); - RootCallTarget callTarget = context.getLanguage().createCachedCallTarget(AtExitRootNode::new, AtExitRootNode.class); - context.registerAtexitHook(callable, arguments, keywords, callTarget); + RootNode rootNode = context.getLanguage().createCachedRootNode(AtExitRootNode::new, AtExitRootNode.class); + context.registerAtexitHook(callable, arguments, keywords, rootNode); return callable; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java index b6810489fd..6c287f845f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java @@ -89,10 +89,8 @@ import static com.oracle.graal.python.nodes.PGuards.isNoValue; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DICT__; import static com.oracle.graal.python.nodes.SpecialMethodNames.T_FILENO; -import static com.oracle.graal.python.nodes.SpecialMethodNames.T___FORMAT__; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___MRO_ENTRIES__; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___ROUND__; -import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; import static com.oracle.graal.python.nodes.StringLiterals.T_FALSE; import static com.oracle.graal.python.nodes.StringLiterals.T_MINUS; import static com.oracle.graal.python.nodes.StringLiterals.T_NEWLINE; @@ -146,7 +144,6 @@ import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKey; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorNext; import com.oracle.graal.python.builtins.objects.common.PHashingCollection; -import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode; 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.ellipsis.EllipsisBuiltins; @@ -167,13 +164,13 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; 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; import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.CallSlotTpIterNextNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotUnaryFunc.CallSlotUnaryNode; import com.oracle.graal.python.compiler.Compiler; import com.oracle.graal.python.compiler.ParserCallbacksImpl; import com.oracle.graal.python.lib.IteratorExhausted; +import com.oracle.graal.python.lib.PyAIterCheckNode; import com.oracle.graal.python.lib.PyBytesCheckNode; import com.oracle.graal.python.lib.PyCallableCheckNode; import com.oracle.graal.python.lib.PyEvalGetGlobals; @@ -188,13 +185,16 @@ import com.oracle.graal.python.lib.PyNumberDivmodNode; import com.oracle.graal.python.lib.PyNumberIndexNode; import com.oracle.graal.python.lib.PyNumberPowerNode; -import com.oracle.graal.python.lib.PyObjectAsciiNode; +import com.oracle.graal.python.lib.PyObjectAsciiAsObjectNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectDir; +import com.oracle.graal.python.lib.PyObjectFormat; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectGetAttrO; import com.oracle.graal.python.lib.PyObjectGetIter; import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyObjectIsInstanceNode; +import com.oracle.graal.python.lib.PyObjectIsSubclassNode; import com.oracle.graal.python.lib.PyObjectIsTrueNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectLookupAttrO; @@ -223,7 +223,6 @@ import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; -import com.oracle.graal.python.nodes.bytecode.GetAIterNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.call.CallDispatchers; @@ -238,6 +237,7 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.PythonBinaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryClinicBuiltinNode; @@ -245,7 +245,6 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode; 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.BuiltinClassProfiles.IsBuiltinClassExactProfile; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.nodes.object.GetOrCreateDictNode; @@ -286,7 +285,6 @@ 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.bytecode.OperationProxy; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -296,7 +294,6 @@ import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateNodeFactory; -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.ReportPolymorphism; @@ -309,8 +306,6 @@ 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.ExplodeLoop.LoopExplosionKind; import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; @@ -895,6 +890,7 @@ static Object eval(VirtualFrame frame, Node inliningTarget, Object source, Objec throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.CODE_OBJ_NO_FREE_VARIABLES, mode); } Object[] args = createArguments.execute(frame, inliningTarget, globals, locals, mode); + PArguments.setCodeObject(args, code); RootCallTarget callTarget = getCallTarget.execute(inliningTarget, code); return invoke.execute(frame, inliningTarget, callTarget, args); } @@ -985,10 +981,9 @@ protected abstract Object executeInternal(VirtualFrame frame, Object source, Tru int featureVersion); private int inheritFlags(VirtualFrame frame, int flags, ReadFrameNode readCallerFrame) { - PFrame fr = readCallerFrame.getCurrentPythonFrame(frame); - if (fr != null) { - PCode code = PFactory.createCode(PythonLanguage.get(this), fr.getTarget()); - flags |= code.getFlags() & PyCF_MASK; + PFrame pyFrame = readCallerFrame.getCurrentPythonFrame(frame); + if (pyFrame != null) { + flags |= pyFrame.getCode().getFlags() & PyCF_MASK; } return flags; } @@ -1030,7 +1025,7 @@ Object compile(TruffleString expression, TruffleString filename, TruffleString m } } if ((flags & PyCF_ONLY_AST) != 0) { - Source source = PythonLanguage.newSource(context, code, filename, mayBeFromFile, PythonLanguage.MIME_TYPE); + Source source = PythonLanguage.newSource(context, code, filename, mayBeFromFile, InputType.FILE, optimize, flags); ParserCallbacksImpl parserCb = new ParserCallbacksImpl(source, PythonOptions.isPExceptionWithJavaStacktrace(getLanguage())); EnumSet compilerFlags = EnumSet.noneOf(AbstractParser.Flags.class); @@ -1054,14 +1049,10 @@ Object compile(TruffleString expression, TruffleString filename, TruffleString m CallTarget ct; TruffleString finalCode = code; Supplier createCode = () -> { - if (type == InputType.FILE) { - Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, PythonLanguage.getCompileMimeType(optimize, flags)); - return context.getEnv().parsePublic(source); - } else if (type == InputType.EVAL) { - Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, PythonLanguage.getEvalMimeType(optimize, flags)); + Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, type, optimize, flags); + if (type != InputType.SINGLE) { return context.getEnv().parsePublic(source); } else { - Source source = PythonLanguage.newSource(context, finalCode, filename, mayBeFromFile, PythonLanguage.MIME_TYPE); boolean allowIncomplete = (flags & PyCF_ALLOW_INCOMPLETE_INPUT) != 0; return context.getLanguage().parse(context, source, InputType.SINGLE, false, optimize, false, allowIncomplete, null, FutureFeature.fromFlags(flags)); } @@ -1071,7 +1062,7 @@ Object compile(TruffleString expression, TruffleString filename, TruffleString m } else { ct = getContext().getLanguage().cacheCode(filename, createCode); } - return wrapRootCallTarget((RootCallTarget) ct); + return wrapRootCallTarget((RootCallTarget) ct, filename); } @Specialization(limit = "3") @@ -1102,7 +1093,7 @@ Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleStri ModTy mod = AstModuleBuiltins.obj2sst(inliningTarget, context, wSource, getParserInputType(mode, flags)); Source source = PythonUtils.createFakeSource(filename); RootCallTarget rootCallTarget = context.getLanguage(inliningTarget).compileModule(context, mod, source, false, optimize, null, null, flags); - return wrapRootCallTarget(rootCallTarget); + return wrapRootCallTarget(rootCallTarget, filename); } TruffleString source = sourceAsString(frame, inliningTarget, wSource, filename, interopLib, acquireLib, bufferLib, switchEncodingNode, raiseNode); checkSource(source); @@ -1112,7 +1103,7 @@ Object generic(VirtualFrame frame, Object wSource, Object wFilename, TruffleStri } } - private static PCode wrapRootCallTarget(RootCallTarget rootCallTarget) { + private static PCode wrapRootCallTarget(RootCallTarget rootCallTarget, TruffleString filename) { RootNode rootNode = rootCallTarget.getRootNode(); if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { if (rootNode instanceof PBytecodeDSLRootNode bytecodeDSLRootNode) { @@ -1121,7 +1112,7 @@ private static PCode wrapRootCallTarget(RootCallTarget rootCallTarget) { } else if (rootNode instanceof PBytecodeRootNode bytecodeRootNode) { bytecodeRootNode.triggerDeferredDeprecationWarnings(); } - return PFactory.createCode(PythonLanguage.get(null), rootCallTarget); + return PFactory.createCode(PythonLanguage.get(null), rootCallTarget, filename); } @TruffleBoundary @@ -1198,7 +1189,7 @@ TruffleString sourceAsString(VirtualFrame frame, Node inliningTarget, Object sou private TruffleString doDecodeSource(Object source, TruffleString filename, byte[] bytes, int bytesLen) { Charset charset = PythonFileDetector.findEncodingStrict(bytes, bytesLen); TruffleString pythonEncoding = CharsetMapping.getPythonEncodingNameFromJavaName(charset.name()); - CodecsModuleBuiltins.TruffleDecoder decoder = new CodecsModuleBuiltins.TruffleDecoder(pythonEncoding, charset, bytes, bytesLen, CodingErrorAction.REPORT); + CodecsModuleBuiltins.TruffleDecoder decoder = new CodecsModuleBuiltins.TruffleDecoder(charset, bytes, bytesLen, CodingErrorAction.REPORT); if (!decoder.decodingStep(true)) { int pos = decoder.getInputPosition(); Object exception = CallNode.executeUncached(PythonBuiltinClassType.UnicodeDecodeError, pythonEncoding, source, pos, pos + decoder.getErrorLength(), decoder.getErrorReason()); @@ -1211,7 +1202,7 @@ private TruffleString doDecodeSource(Object source, TruffleString filename, byte private RuntimeException raiseInvalidSyntax(TruffleString filename, String format, Object... args) { PythonContext context = getContext(); // Create non-empty source to avoid overwriting the message with "unexpected EOF" - Source source = PythonLanguage.newSource(context, T_SPACE, filename, mayBeFromFile, null); + Source source = PythonLanguage.newSource(context, T_SPACE, filename, mayBeFromFile, InputType.FILE, 0, 0); SourceRange sourceRange = new SourceRange(1, 0, 1, 0); TruffleString message = toTruffleStringUncached(String.format(format, args)); throw raiseSyntaxError(ParserCallbacks.ErrorType.Syntax, sourceRange, message, source, PythonOptions.isPExceptionWithJavaStacktrace(context.getLanguage())); @@ -1283,207 +1274,27 @@ Object doObject(Object value, } } - /** - * Base class for {@code isinstance} and {@code issubclass} that implements the recursive - * iteration of tuples passed as the second argument. The inheriting classes need to just - * provide the base for the recursion. - */ - public abstract static class RecursiveBinaryCheckBaseNode extends PythonBinaryBuiltinNode { - static final int MAX_EXPLODE_LOOP = 16; // is also shifted to the left by recursion depth - static final byte NON_RECURSIVE = Byte.MAX_VALUE; - - protected final byte depth; - - protected RecursiveBinaryCheckBaseNode(byte depth) { - this.depth = depth; - } - - public abstract boolean executeWith(VirtualFrame frame, Object instance, Object cls); - - @NeverDefault - protected final RecursiveBinaryCheckBaseNode createRecursive() { - return createRecursive((byte) (depth + 1)); - } - - @NeverDefault - protected final RecursiveBinaryCheckBaseNode createNonRecursive() { - return createRecursive(NON_RECURSIVE); - } - - protected RecursiveBinaryCheckBaseNode createRecursive(@SuppressWarnings("unused") byte newDepth) { - throw new AbstractMethodError(); // Cannot be really abstract b/c Truffle DSL... - } - - @Idempotent - protected int getMaxExplodeLoop() { - return MAX_EXPLODE_LOOP >> depth; - } - - @Specialization(guards = {"depth < getNodeRecursionLimit()", "getLength(clsTuple) == cachedLen", "cachedLen < getMaxExplodeLoop()"}, // - limit = "getVariableArgumentInlineCacheLimit()") - @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN) - static boolean doTupleConstantLen(VirtualFrame frame, Object instance, PTuple clsTuple, - @Bind Node inliningTarget, - @Cached("getLength(clsTuple)") int cachedLen, - @Shared @Cached GetObjectArrayNode getObjectArrayNode, - @Shared @Cached("createRecursive()") RecursiveBinaryCheckBaseNode recursiveNode) { - Object[] array = getObjectArrayNode.execute(inliningTarget, clsTuple); - for (int i = 0; i < cachedLen; i++) { - Object cls = array[i]; - if (recursiveNode.executeWith(frame, instance, cls)) { - return true; - } - } - return false; - } - - @Specialization(guards = "depth < getNodeRecursionLimit()", replaces = "doTupleConstantLen") - static boolean doRecursiveWithNode(VirtualFrame frame, Object instance, PTuple clsTuple, - @Bind Node inliningTarget, - @Shared @Cached GetObjectArrayNode getObjectArrayNode, - @Shared @Cached("createRecursive()") RecursiveBinaryCheckBaseNode recursiveNode) { - for (Object cls : getObjectArrayNode.execute(inliningTarget, clsTuple)) { - if (recursiveNode.executeWith(frame, instance, cls)) { - return true; - } - } - return false; - } - - @Specialization(guards = {"depth != NON_RECURSIVE", "depth >= getNodeRecursionLimit()"}) - static boolean doRecursiveWithLoop(VirtualFrame frame, Object instance, PTuple clsTuple, - @Bind Node inliningTarget, - @Cached("createFor($node)") BoundaryCallData boundaryCallData, - @Shared @Cached GetObjectArrayNode getObjectArrayNode, - @Cached("createNonRecursive()") RecursiveBinaryCheckBaseNode node) { - PythonContext context = PythonContext.get(inliningTarget); - PythonLanguage language = context.getLanguage(inliningTarget); - Object state = BoundaryCallContext.enter(frame, language, context, boundaryCallData); - try { - // Note: we need actual recursion to trigger the stack overflow error like CPython - // Note: we need fresh RecursiveBinaryCheckBaseNode and cannot use "this", because - // other children of this executed by other specializations may assume they'll - // always get a non-null frame - return callRecursiveWithNodeTruffleBoundary(inliningTarget, instance, clsTuple, getObjectArrayNode, node); - } finally { - BoundaryCallContext.exit(frame, language, context, state); - } - } - - @Specialization(guards = "depth == NON_RECURSIVE") - boolean doRecursiveWithLoopReuseThis(VirtualFrame frame, Object instance, PTuple clsTuple, - @Bind Node inliningTarget, - @Shared @Cached GetObjectArrayNode getObjectArrayNode) { - // This should be only called by doRecursiveWithLoop, now we have to reuse this to stop - // recursive node creation. It is OK, because now all specializations should always get - // null frame - assert frame == null; - return callRecursiveWithNodeTruffleBoundary(inliningTarget, instance, clsTuple, getObjectArrayNode, this); - } - - @TruffleBoundary - private static boolean callRecursiveWithNodeTruffleBoundary(Node inliningTarget, Object instance, PTuple clsTuple, GetObjectArrayNode getObjectArrayNode, RecursiveBinaryCheckBaseNode node) { - return doRecursiveWithNode(null, instance, clsTuple, inliningTarget, getObjectArrayNode, node); - } - - protected static int getLength(PTuple t) { - return t.getSequenceStorage().length(); - } - } - // isinstance(object, classinfo) @Builtin(name = J_ISINSTANCE, minNumOfPositionalArgs = 2) @GenerateNodeFactory - public abstract static class IsInstanceNode extends RecursiveBinaryCheckBaseNode { + abstract static class IsInstanceNode extends PythonBinaryBuiltinNode { - protected IsInstanceNode(byte depth) { - super(depth); - } - - protected IsInstanceNode() { - this((byte) 0); - } - - @Override - public IsInstanceNode createRecursive(byte newDepth) { - return BuiltinFunctionsFactory.IsInstanceNodeFactory.create(newDepth); - } - - @Specialization(guards = "!isPTuple(cls)") - static boolean isInstance(VirtualFrame frame, Object instance, Object cls, - @Bind Node inliningTarget, - @Cached GetClassNode getClsClassNode, - @Cached IsBuiltinClassExactProfile classProfile, - @Cached GetClassNode getInstanceClassNode, - @Cached TypeNodes.IsSameTypeNode isSameTypeNode, - @Cached("create(T___INSTANCECHECK__)") LookupAndCallBinaryNode instanceCheckNode, - @Cached PyObjectIsTrueNode isTrueNode, - @Cached TypeNodes.GenericInstanceCheckNode genericInstanceCheckNode, - @Cached InlinedBranchProfile noInstanceCheckProfile) { - if (isSameTypeNode.execute(inliningTarget, getInstanceClassNode.execute(inliningTarget, instance), cls)) { - // Exact match, don't call __instancecheck__ - return true; - } - if (classProfile.profileClass(inliningTarget, getClsClassNode.execute(inliningTarget, cls), PythonBuiltinClassType.PythonClass)) { - // Avoid the lookup and call overhead when we know we're calling - // type.__instancecheck__ - return genericInstanceCheckNode.execute(frame, inliningTarget, instance, cls); - } - try { - Object result = instanceCheckNode.executeObject(frame, cls, instance); - return isTrueNode.execute(frame, result); - } catch (SpecialMethodNotFound ignore) { - noInstanceCheckProfile.enter(inliningTarget); - return genericInstanceCheckNode.execute(frame, inliningTarget, instance, cls); - } + @Specialization + static Object isInstance(VirtualFrame frame, Object instance, Object cls, + @Cached PyObjectIsInstanceNode isInstanceNode) { + return isInstanceNode.execute(frame, instance, cls); } } // issubclass(class, classinfo) @Builtin(name = J_ISSUBCLASS, minNumOfPositionalArgs = 2) @GenerateNodeFactory - public abstract static class IsSubClassNode extends RecursiveBinaryCheckBaseNode { - public abstract boolean executeBoolean(VirtualFrame frame, Object derived, Object cls); - - protected IsSubClassNode(byte depth) { - super(depth); - } - - protected IsSubClassNode() { - this((byte) 0); - } - - @Override - public IsSubClassNode createRecursive(byte newDepth) { - return BuiltinFunctionsFactory.IsSubClassNodeFactory.create(newDepth); - } - - @Specialization(guards = "!isPTuple(cls)") - static boolean isSubclass(VirtualFrame frame, Object derived, Object cls, - @Bind Node inliningTarget, - @Cached GetClassNode getClsClassNode, - @Cached IsBuiltinClassExactProfile classProfile, - @Cached("create(T___SUBCLASSCHECK__)") LookupAndCallBinaryNode subclassCheckNode, - @Cached PyObjectIsTrueNode isTrueNode, - @Cached TypeNodes.GenericSubclassCheckNode genericSubclassCheckNode, - @Cached InlinedBranchProfile noInstanceCheckProfile) { - if (classProfile.profileClass(inliningTarget, getClsClassNode.execute(inliningTarget, cls), PythonBuiltinClassType.PythonClass)) { - // Avoid the lookup and call overhead when we know we're calling - // type.__subclasscheck__ - return genericSubclassCheckNode.execute(frame, inliningTarget, derived, cls); - } - try { - Object result = subclassCheckNode.executeObject(frame, cls, derived); - return isTrueNode.execute(frame, result); - } catch (SpecialMethodNotFound ignore) { - noInstanceCheckProfile.enter(inliningTarget); - return genericSubclassCheckNode.execute(frame, inliningTarget, derived, cls); - } - } + abstract static class IsSubClassNode extends PythonBinaryBuiltinNode { - @NeverDefault - public static IsSubClassNode create() { - return BuiltinFunctionsFactory.IsSubClassNodeFactory.create(); + @Specialization + static Object isSubclass(VirtualFrame frame, Object derived, Object cls, + @Cached PyObjectIsSubclassNode isSubclassNode) { + return isSubclassNode.execute(frame, derived, cls); } } @@ -1883,27 +1694,18 @@ static Object repr(VirtualFrame frame, Object obj, // format(object, [format_spec]) @Builtin(name = J_FORMAT, minNumOfPositionalArgs = 1, parameterNames = {"object", "format_spec"}) + @ArgumentClinic(name = "format_spec", conversion = ArgumentClinic.ClinicConversion.TString, defaultValue = "T_EMPTY_STRING") @GenerateNodeFactory - @OperationProxy.Proxyable(storeBytecodeIndex = true) - @ImportStatic(PGuards.class) - public abstract static class FormatNode extends PythonBinaryBuiltinNode { + abstract static class FormatNode extends PythonBinaryClinicBuiltinNode { + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return BuiltinFunctionsClinicProviders.FormatNodeClinicProviderGen.INSTANCE; + } @Specialization - public static Object format(VirtualFrame frame, Object obj, Object formatSpec, - @Bind Node inliningTarget, - @Cached("create(T___FORMAT__)") LookupAndCallBinaryNode callFormat, - @Cached InlinedConditionProfile formatIsNoValueProfile, - @Cached PRaiseNode raiseNode) { - Object format = formatIsNoValueProfile.profile(inliningTarget, isNoValue(formatSpec)) ? T_EMPTY_STRING : formatSpec; - try { - Object res = callFormat.executeObject(frame, obj, format); - if (!PGuards.isString(res)) { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.S_MUST_RETURN_S_NOT_P, T___FORMAT__, "str", res); - } - return res; - } catch (SpecialMethodNotFound ignore) { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_FORMAT, obj); - } + public static Object format(VirtualFrame frame, Object obj, TruffleString formatSpec, + @Cached PyObjectFormat formatNode) { + return formatNode.execute(frame, obj, formatSpec); } @NeverDefault @@ -1918,9 +1720,9 @@ public static FormatNode create() { abstract static class AsciiNode extends PythonUnaryBuiltinNode { @Specialization - public static TruffleString ascii(VirtualFrame frame, Object obj, + public static Object ascii(VirtualFrame frame, Object obj, @Bind Node inliningTarget, - @Cached PyObjectAsciiNode asciiNode) { + @Cached PyObjectAsciiAsObjectNode asciiNode) { return asciiNode.execute(frame, inliningTarget, obj); } } @@ -1934,11 +1736,11 @@ static Object round(VirtualFrame frame, Object x, @SuppressWarnings("unused") PN @Bind Node inliningTarget, @Cached("create(T___ROUND__)") LookupAndCallUnaryNode callRound, @Shared @Cached PRaiseNode raiseNode) { - Object result = callRound.executeObject(frame, x); - if (result == PNone.NO_VALUE) { + try { + return callRound.executeObject(frame, x); + } catch (SpecialMethodNotFound e) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_METHOD, x, T___ROUND__); } - return result; } @Specialization(guards = "!isPNone(n)") @@ -2602,8 +2404,22 @@ static Object doGeneric(VirtualFrame frame, Object asyncIter, Object defaultValu public abstract static class AIter extends PythonUnaryBuiltinNode { @Specialization static Object doGeneric(VirtualFrame frame, Object arg, - @Cached GetAIterNode aiter) { - return aiter.execute(frame, arg); + @Bind Node inliningTarget, + @Cached GetObjectSlotsNode getSlots, + @Cached CallSlotUnaryNode callSlot, + @Cached PyAIterCheckNode checkNode, + @Cached GetClassNode getClassNode, + @Cached PRaiseNode raiseNode) { + TpSlots slots = getSlots.execute(inliningTarget, arg); + if (slots.am_aiter() == null) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.OBJECT_NOT_ASYNC_ITERABLE, arg); + } + Object asyncIterator = callSlot.execute(frame, inliningTarget, slots.am_aiter(), arg); + if (!checkNode.execute(inliningTarget, asyncIterator)) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.AITER_RETURNED_NOT_ASYNC_ITERATOR, + getClassNode.execute(inliningTarget, asyncIterator)); + } + return asyncIterator; } } @@ -2611,6 +2427,7 @@ static Object doGeneric(VirtualFrame frame, Object arg, @Builtin(name = "input", minNumOfPositionalArgs = 0, parameterNames = {"prompt"}) @GenerateNodeFactory abstract static class InputNode extends PythonUnaryBuiltinNode { + private static final TruffleString T_BUILTINS_INPUT = tsLiteral("builtins.input"); @Specialization static Object input(VirtualFrame frame, Object prompt, @@ -2643,7 +2460,7 @@ private static Object doInput(Object prompt, Node node, PythonContext context) { throw PRaiseNode.raiseStatic(node, RuntimeError, ErrorMessages.INPUT_LOST_SYS_S, T_STDERR); } - SysModuleBuiltins.AuditNode.auditUncached("builtins.input", prompt != NO_VALUE ? prompt : NONE); + SysModuleBuiltins.AuditNode.auditUncached(T_BUILTINS_INPUT, prompt != NO_VALUE ? prompt : NONE); try { PyObjectCallMethodObjArgs.executeUncached(stderr, T_FLUSH); 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 f8e1888cf1..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 @@ -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 @@ -72,6 +72,7 @@ import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; @@ -133,7 +134,9 @@ import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage; import com.oracle.graal.python.util.CharsetMapping; import com.oracle.graal.python.util.CharsetMapping.NormalizeEncodingNameNode; +import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.HostCompilerDirectives; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; @@ -182,6 +185,44 @@ protected List> getNodeFa return CodecsModuleBuiltinsFactory.getFactories(); } + @GenerateUncached + @GenerateInline + public abstract static class CharsetLookupNode extends Node { + public abstract CharsetMapping.CharsetWrapper execute(Node inliningTarget, TruffleString name); + + @SuppressWarnings("unused") + @Specialization(guards = "name == cachedName", limit = "1") + static CharsetMapping.CharsetWrapper doCachedIdentity(TruffleString name, + @Cached("name") TruffleString cachedName, + @Cached("lookup(name)") CharsetMapping.CharsetWrapper cachedResult) { + return cachedResult; + } + + @SuppressWarnings("unused") + @Specialization(guards = "equals(name, cachedName, equalNode)", limit = "1", replaces = "doCachedIdentity") + static CharsetMapping.CharsetWrapper doCachedEqual(TruffleString name, + @Cached("name") TruffleString cachedName, + @Cached("lookup(name)") CharsetMapping.CharsetWrapper cachedResult, + @Cached TruffleString.EqualNode equalNode) { + return cachedResult; + } + + @Specialization(replaces = "doCachedEqual") + static CharsetMapping.CharsetWrapper doDynamic(Node inliningTarget, TruffleString name, + @Cached NormalizeEncodingNameNode normalizeEncodingNameNode) { + return CharsetMapping.getCharsetNormalized(normalizeEncodingNameNode.execute(inliningTarget, name)); + } + + @SuppressWarnings("unused") + static CharsetMapping.CharsetWrapper lookup(TruffleString name) { + return CharsetMapping.getCharsetNormalized(CharsetMapping.normalizeUncached(name)); + } + + static boolean equals(TruffleString a, TruffleString b, TruffleString.EqualNode equalNode) { + return equalNode.execute(a, b, TS_ENCODING); + } + } + @GenerateUncached @GenerateInline(false) // footprint reduction 48 -> 30 public abstract static class CodecsEncodeToJavaBytesNode extends Node { @@ -191,6 +232,11 @@ public abstract static class CodecsEncodeToJavaBytesNode extends Node { byte[] encode(VirtualFrame frame, Object self, TruffleString encoding, TruffleString errors, @Bind Node inliningTarget, @Cached CastToTruffleStringNode castTruffleStr, + @Cached TruffleString.IsValidNode isValidNode, + @Cached TruffleString.GetCodeRangeNode getCodeRangeNode, + @Cached InlinedConditionProfile fastPathProfile, + @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode, @Cached TruffleString.ToJavaStringNode toJavaStringNode, @Cached TruffleString.EqualNode equalNode, @Cached ErrorHandlers.CallEncodingErrorHandlerNode errorHandler, @@ -198,17 +244,67 @@ byte[] encode(VirtualFrame frame, Object self, TruffleString encoding, TruffleSt @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib, @Cached CastToJavaStringNode castToJavaStringNode, @Cached PRaiseNode raiseNode, - @Cached NormalizeEncodingNameNode normalizeEncodingNameNode) { + @Cached(inline = true) CharsetLookupNode charsetLookupNode) { TruffleString input = castTruffleStr.castKnownString(inliningTarget, self); - String inputStr = toJavaStringNode.execute(input); - CodingErrorAction errorAction = convertCodingErrorAction(errors, equalNode); - TruffleString normalizedEncoding = normalizeEncodingNameNode.execute(inliningTarget, encoding); - Charset charset = CharsetMapping.getCharsetNormalized(normalizedEncoding); - if (charset == null) { + CharsetMapping.CharsetWrapper charsetWrapper = charsetLookupNode.execute(inliningTarget, encoding); + if (charsetWrapper == null) { throw raiseNode.raise(inliningTarget, LookupError, ErrorMessages.UNKNOWN_ENCODING, encoding); } + TruffleString.Encoding targetTStringEncoding = charsetWrapper.tStringEncoding(); + if (fastPathProfile.profile(inliningTarget, isValidNode.execute(input, TS_ENCODING) && targetTStringEncoding != null)) { + byte[] ret = fastPath(input, getCodeRangeNode, switchEncodingNode, copyToByteArrayNode, targetTStringEncoding, charsetWrapper); + if (ret != null) { + return ret; + } + } + return slowPath(frame, encoding, errors, inliningTarget, toJavaStringNode, equalNode, errorHandler, acquireLib, bufferLib, castToJavaStringNode, raiseNode, input, charsetWrapper); + } + + private static byte[] fastPath(TruffleString input, + TruffleString.GetCodeRangeNode getCodeRangeNode, + TruffleString.SwitchEncodingNode switchEncodingNode, + TruffleString.CopyToByteArrayNode copyToByteArrayNode, + TruffleString.Encoding targetTStringEncoding, + CharsetMapping.CharsetWrapper charsetWrapper) { + if (targetTStringEncoding == TruffleString.Encoding.US_ASCII || targetTStringEncoding == TruffleString.Encoding.ISO_8859_1) { + TruffleString.CodeRange codeRange = getCodeRangeNode.execute(input, TS_ENCODING); + if (codeRange.isSupersetOf(targetTStringEncoding == TruffleString.Encoding.US_ASCII ? TruffleString.CodeRange.LATIN_1 : TruffleString.CodeRange.BMP)) { + // string contains characters that cannot be represented in ASCII / LATIN-1. + // defer to slow path + return null; + } + } + TruffleString transcoded = switchEncodingNode.execute(input, targetTStringEncoding); + CharsetMapping.BOM bom = charsetWrapper.bom(); + byte[] ret = new byte[transcoded.byteLength(targetTStringEncoding) + (bom == null ? 0 : bom.bytes.length)]; + int startIndex; + if (bom == null) { + startIndex = 0; + } else { + System.arraycopy(bom.bytes, 0, ret, 0, bom.bytes.length); + startIndex = bom.bytes.length; + } + copyToByteArrayNode.execute(transcoded, 0, ret, startIndex, transcoded.byteLength(targetTStringEncoding), targetTStringEncoding); + return ret; + } + + @HostCompilerDirectives.InliningCutoff + private byte[] slowPath(VirtualFrame frame, TruffleString encoding, TruffleString errors, + Node inliningTarget, + TruffleString.ToJavaStringNode toJavaStringNode, + TruffleString.EqualNode equalNode, + ErrorHandlers.CallEncodingErrorHandlerNode errorHandler, + PythonBufferAcquireLibrary acquireLib, + PythonBufferAccessLibrary bufferLib, + CastToJavaStringNode castToJavaStringNode, + PRaiseNode raiseNode, + TruffleString input, + CharsetMapping.CharsetWrapper charsetWrapper) { + String inputStr = toJavaStringNode.execute(input); + CodingErrorAction errorAction = convertCodingErrorAction(errors, equalNode); TruffleEncoder encoder; ErrorHandlers.ErrorHandlerCache errorHandlerCache = new ErrorHandlers.ErrorHandlerCache(); + Charset charset = charsetWrapper.charset(); try { encoder = new TruffleEncoder(charset, inputStr, errorAction); while (!encoder.encodingStep()) { @@ -290,8 +386,12 @@ static Object decode(VirtualFrame frame, Object input, TruffleString encoding, T @Cached("createFor($node)") InteropCallData callData, @CachedLibrary(limit = "3") PythonBufferAcquireLibrary acquireLib, @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib, + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Cached TruffleString.IsValidNode isValidNode, + @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Cached InlinedConditionProfile fastPathProfile, @Cached TruffleString.EqualNode equalNode, - @Cached NormalizeEncodingNameNode normalizeEncodingNameNode, + @Cached(inline = true) CharsetLookupNode charsetLookupNode, @Cached ErrorHandlers.CallDecodingErrorHandlerNode callDecodingErrorHandlerNode, @Cached TruffleString.ToJavaStringNode toJavaStringNode, @Cached InlinedBranchProfile inputReplaced, @@ -300,16 +400,56 @@ static Object decode(VirtualFrame frame, Object input, TruffleString encoding, T try { int len = bufferLib.getBufferLength(buffer); byte[] bytes = bufferLib.getInternalOrCopiedByteArray(buffer); - CodingErrorAction errorAction = convertCodingErrorAction(errors, equalNode); - TruffleString normalizedEncoding = normalizeEncodingNameNode.execute(inliningTarget, encoding); - Charset charset = CharsetMapping.getCharsetForDecodingNormalized(normalizedEncoding, bytes, len); + CharsetMapping.CharsetWrapper charset = charsetLookupNode.execute(inliningTarget, encoding); if (charset == null) { throw raiseNode.raise(inliningTarget, LookupError, ErrorMessages.UNKNOWN_ENCODING, encoding); } + CharsetMapping.BOM bom = charset.bom(); + int offset = 0; + if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN && bom != null) { + /* + * JDK's charsets for UTF-16 and UTF-32 default to big endian irrespective of + * the platform if there is no BOM. The UTF-16-LE and UTF-32-LE charsets reject + * big endian BOM. CPython defaults to platform endian and accepts both BOMs. + * So, in order to get the behavior we need, we have to take a peek at the + * possible BOM and if it has a BOM use the UTF-16/32 encoding and let it + * detect, otherwise default to UTF-16/32-LE. + */ + if (charset == CharsetMapping.UTF_16LE_BOM) { + if (len >= 2) { + short first = PythonUtils.ARRAY_ACCESSOR.getShort(bytes, 0); + if (first == (short) 0xFFFE) { + charset = CharsetMapping.UTF_16BE_BOM; + offset = 2; + } else if (first == (short) 0xFEFF) { + offset = 2; + } + } + } else { + assert charset == CharsetMapping.UTF_32LE_BOM; + if (len >= 4) { + int first = PythonUtils.ARRAY_ACCESSOR.getInt(bytes, 0); + if (first == 0xFFFE0000) { + charset = CharsetMapping.UTF_32BE_BOM; + offset = 4; + } else if (first == 0x0000FEFF) { + offset = 4; + } + } + } + } + TruffleString.Encoding tStringEncoding = charset.tStringEncoding(); + if (tStringEncoding != null && (len & (charset.stride() - 1)) == 0) { + TruffleString direct = fromByteArrayNode.execute(bytes, offset, len - offset, tStringEncoding, true); + if (fastPathProfile.profile(inliningTarget, isValidNode.execute(direct, tStringEncoding))) { + return PFactory.createTuple(language, new Object[]{switchEncodingNode.execute(direct, TS_ENCODING), len}); + } + } + CodingErrorAction errorAction = convertCodingErrorAction(errors, equalNode); ErrorHandlers.ErrorHandlerCache handlerCache = new ErrorHandlers.ErrorHandlerCache(); TruffleDecoder decoder; try { - decoder = new TruffleDecoder(normalizedEncoding, charset, bytes, len, errorAction); + decoder = new TruffleDecoder(charset.charset(), bytes, len, errorAction); while (!decoder.decodingStep(finalData)) { int pos = decoder.getInputPosition(); ErrorHandlers.DecodingErrorHandlerResult result = callDecodingErrorHandlerNode.execute(frame, inliningTarget, handlerCache, errors, encoding, input, @@ -588,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, @@ -1367,15 +1512,13 @@ public void replace(String replacement, Charset charset) { } static class TruffleDecoder { - private final TruffleString encodingName; private final CharsetDecoder decoder; private ByteBuffer inputBuffer; private CharBuffer outputBuffer; private CoderResult coderResult; @TruffleBoundary - public TruffleDecoder(TruffleString encodingName, Charset charset, byte[] input, int inputLen, CodingErrorAction errorAction) { - this.encodingName = encodingName; + public TruffleDecoder(Charset charset, byte[] input, int inputLen, CodingErrorAction errorAction) { this.inputBuffer = ByteBuffer.wrap(input, 0, inputLen); this.decoder = charset.newDecoder().onMalformedInput(errorAction).onUnmappableCharacter(errorAction); this.outputBuffer = CharBuffer.allocate((int) (inputLen * decoder.averageCharsPerByte())); @@ -1473,8 +1616,5 @@ public void replace(int skipInput, char[] chars, int offset, int length) { inputBuffer.position(inputBuffer.position() + skipInput); } - public TruffleString getEncodingName() { - return encodingName; - } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java index dc2a30be35..e77cf9e3dc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.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 @@ -52,6 +52,11 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; @@ -69,16 +74,20 @@ import com.oracle.graal.python.lib.PyTimeFromObjectNode; import com.oracle.graal.python.lib.PyTimeFromObjectNode.RoundType; import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode; 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.PythonUnaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; +import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.ExceptionUtils; @@ -95,6 +104,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @@ -121,7 +131,7 @@ public void postInitialize(Python3Core core) { } @TruffleBoundary - private static void dumpTraceback(PythonLanguage language, PrintWriter writer) { + private static synchronized void dumpTraceback(PythonLanguage language, PrintWriter writer) { writer.println(); writer.println(Thread.currentThread()); if (PythonOptions.isPExceptionWithJavaStacktrace(language)) { @@ -150,7 +160,7 @@ PNone doit(VirtualFrame frame, Object fileObj, boolean allThreads, Object state = BoundaryCallContext.enter(frame, language, context, boundaryCallData); try { // it's not important for this to be fast at all - dump(language, context, fileno, fileObj, allThreads); + dump(inliningTarget, language, context, fileno, fileObj, allThreads); } finally { BoundaryCallContext.exit(frame, language, context, state); } @@ -159,14 +169,15 @@ PNone doit(VirtualFrame frame, Object fileObj, boolean allThreads, } @TruffleBoundary - static void dump(PythonLanguage language, PythonContext context, int fd, Object fileObj, boolean allThreads) { + static void dump(Node inliningTarget, PythonLanguage language, PythonContext context, int fd, Object fileObj, boolean allThreads) { if (allThreads) { - context.getEnv().submitThreadLocal(context.getThreads(), new ThreadLocalAction(true, false) { + Future future = context.getEnv().submitThreadLocal(context.getThreads(), new ThreadLocalAction(true, false) { @Override protected void perform(ThreadLocalAction.Access access) { dumpTraceback(language, newRawFdPrintWriter(fd)); } }); + waitForDumpTraceback(inliningTarget, future); } else { dumpTraceback(language, newRawFdPrintWriter(fd)); } @@ -174,6 +185,29 @@ protected void perform(ThreadLocalAction.Access access) { Reference.reachabilityFence(fileObj); } + private static void waitForDumpTraceback(Node inliningTarget, Future future) { + GilNode gil = GilNode.getUncached(); + boolean gilReleased = gil.tryRelease(); + try { + TruffleSafepoint.setBlockedThreadInterruptible(inliningTarget, voidFuture -> { + try { + voidFuture.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.RuntimeError, + tsLiteral("Failed to dump traceback from all threads within 10 seconds")); + } catch (CancellationException e) { + // Ignore + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + }, future); + } finally { + if (gilReleased) { + gil.acquire(PythonContext.get(null), inliningTarget); + } + } + } + @Override protected ArgumentClinicProvider getArgumentClinic() { return FaulthandlerModuleBuiltinsClinicProviders.DumpTracebackNodeClinicProviderGen.INSTANCE; @@ -235,6 +269,60 @@ static int fileno(VirtualFrame frame, Node inliningTarget, Object file, } } + @Builtin(name = "_sigsegv", minNumOfPositionalArgs = 0, parameterNames = {"release_gil"}) + @ArgumentClinic(name = "release_gil", conversion = ArgumentClinic.ClinicConversion.Boolean, defaultValue = "false") + @GenerateNodeFactory + abstract static class SigSegvNode extends PythonUnaryClinicBuiltinNode { + @Specialization + static PNone doIt(VirtualFrame frame, boolean releaseGil, + @Bind PythonContext context, + @Bind Node inliningTarget, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached GilNode gil, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + return raiseFatalSignal(frame, context, inliningTarget, posixLib, gil, constructAndRaiseNode, "SEGV", releaseGil); + } + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return FaulthandlerModuleBuiltinsClinicProviders.SigSegvNodeClinicProviderGen.INSTANCE; + } + } + + @Builtin(name = "_sigabrt", minNumOfPositionalArgs = 0) + @GenerateNodeFactory + abstract static class SigAbrtNode extends PythonBuiltinNode { + @Specialization + static PNone doIt(VirtualFrame frame, + @Bind PythonContext context, + @Bind Node inliningTarget, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached GilNode gil, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + return raiseFatalSignal(frame, context, inliningTarget, posixLib, gil, constructAndRaiseNode, "ABRT", false); + } + } + + private static PNone raiseFatalSignal(VirtualFrame frame, PythonContext context, Node inliningTarget, PosixSupportLibrary posixLib, GilNode gil, + PConstructAndRaiseNode.Lazy constructAndRaiseNode, String signalName, boolean releaseGil) { + try { + int signum = SignalModuleBuiltins.signalFromName(context, signalName); + if (releaseGil) { + gil.release(true); + } + try { + posixLib.signalSelf(context.getPosixSupport(), signum); + } finally { + if (releaseGil) { + gil.acquire(); + } + } + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); + } + return PNone.NONE; + } + @Builtin(name = "dump_traceback_later", minNumOfPositionalArgs = 2, declaresExplicitSelf = true, parameterNames = {"$mod", "timeout", "repeat", "file", "exit"}) @ArgumentClinic(name = "repeat", conversion = ArgumentClinic.ClinicConversion.IntToBoolean, defaultValue = "false") @ArgumentClinic(name = "exit", conversion = ArgumentClinic.ClinicConversion.IntToBoolean, defaultValue = "false") @@ -262,9 +350,11 @@ private static void doDumpLater(Node inliningTarget, PythonModule module, long t do { sleepInterruptibly(inliningTarget, timeoutNs); long timeoutS = timeoutNs / 1_000_000_000; - newRawFdPrintWriter(fd).printf("Timeout (%d:%02d:%02d)!%n", timeoutS / 3600, timeoutS / 60, timeoutS); + PrintWriter timeoutWriter = newRawFdPrintWriter(fd); + timeoutWriter.printf("Timeout (%d:%02d:%02d)!%n", timeoutS / 3600, timeoutS / 60, timeoutS); + timeoutWriter.flush(); try { - DumpTracebackNode.dump(context.getLanguage(), context, fd, fileObj, true); + DumpTracebackNode.dump(inliningTarget, context.getLanguage(), context, fd, fileObj, true); if (exit) { // Sleep for a bit to give time for the safepoint-based // printing to execute before we exit diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FcntlModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FcntlModuleBuiltins.java index 49940be0e3..dc42e3f9ce 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FcntlModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FcntlModuleBuiltins.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 @@ -48,6 +48,7 @@ import static com.oracle.graal.python.runtime.PosixConstants.LOCK_NB; import static com.oracle.graal.python.runtime.PosixConstants.LOCK_SH; import static com.oracle.graal.python.runtime.PosixConstants.LOCK_UN; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.util.List; @@ -94,6 +95,9 @@ @CoreFunctions(defineModule = "fcntl") public final class FcntlModuleBuiltins extends PythonBuiltins { + private static final TruffleString T_FCNTL_FLOCK = tsLiteral("fcntl.flock"); + private static final TruffleString T_FCNTL_LOCKF = tsLiteral("fcntl.lockf"); + private static final TruffleString T_FCNT_IOCTL = tsLiteral("fcnt.ioctl"); @Override protected List> getNodeFactories() { @@ -131,7 +135,7 @@ synchronized PNone flock(VirtualFrame frame, int fd, int operation, @Cached SysModuleBuiltins.AuditNode auditNode, @CachedLibrary("getPosixSupport()") PosixSupportLibrary posix, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "fcntl.flock", fd, operation); + auditNode.audit(frame, inliningTarget, T_FCNTL_FLOCK, fd, operation); try { posix.flock(getPosixSupport(), fd, operation); } catch (PosixException e) { @@ -155,7 +159,7 @@ PNone lockf(VirtualFrame frame, int fd, int code, Object lenObj, Object startObj @Cached PyLongAsLongNode asLongNode, @Cached PRaiseNode raiseNode, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "fcntl.lockf", fd, code, lenObj != PNone.NO_VALUE ? lenObj : PNone.NONE, startObj != PNone.NO_VALUE ? startObj : PNone.NONE, whence); + auditNode.audit(frame, inliningTarget, T_FCNTL_LOCKF, fd, code, lenObj != PNone.NO_VALUE ? lenObj : PNone.NONE, startObj != PNone.NO_VALUE ? startObj : PNone.NONE, whence); int lockType; if (code == LOCK_UN.value) { lockType = F_UNLCK.getValueIfDefined(); @@ -212,105 +216,101 @@ Object ioctl(VirtualFrame frame, int fd, long request, Object arg, boolean mutat @Cached PRaiseNode raiseNode, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Cached SysModuleBuiltins.AuditNode auditNode) { - auditNode.audit(inliningTarget, "fcnt.ioctl", fd, request, arg); + auditNode.audit(frame, inliningTarget, T_FCNT_IOCTL, fd, request, arg); - try { - int intArg = 0; - if (arg != PNone.NO_VALUE) { - Object buffer = null; - // Buffer argument - if (acquireLib.hasBuffer(arg)) { - boolean writable = false; + int intArg = 0; + if (arg != PNone.NO_VALUE) { + Object buffer = null; + // Buffer argument + if (acquireLib.hasBuffer(arg)) { + boolean writable = false; + try { + buffer = acquireLib.acquireWritable(arg, frame, callData); + writable = true; + } catch (PException e) { try { - buffer = acquireLib.acquireWritable(arg, frame, callData); - writable = true; - } catch (PException e) { - try { - buffer = acquireLib.acquireReadonly(arg, frame, callData); - } catch (PException e1) { - // ignore - } + buffer = acquireLib.acquireReadonly(arg, frame, callData); + } catch (PException e1) { + // ignore } - if (buffer != null) { - try { - int len = bufferLib.getBufferLength(buffer); - boolean writeBack = false; - boolean releaseGil = true; - byte[] ioctlArg = null; - if (writable && mutateArg) { - writeBack = true; - if (bufferLib.hasInternalByteArray(buffer)) { - byte[] internalArray = bufferLib.getInternalByteArray(buffer); - if (internalArray.length > len && internalArray[len] == 0) { - writeBack = false; - releaseGil = false; // Could resize concurrently - ioctlArg = internalArray; - } - } - } else { - if (len > IOCTL_BUFSZ) { - throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.IOCTL_STRING_ARG_TOO_LONG); + } + if (buffer != null) { + try { + int len = bufferLib.getBufferLength(buffer); + boolean writeBack = false; + boolean releaseGil = true; + byte[] ioctlArg = null; + if (writable && mutateArg) { + writeBack = true; + if (bufferLib.hasInternalByteArray(buffer)) { + byte[] internalArray = bufferLib.getInternalByteArray(buffer); + if (internalArray.length > len && internalArray[len] == 0) { + writeBack = false; + releaseGil = false; // Could resize concurrently + ioctlArg = internalArray; } } - if (ioctlArg == null) { - ioctlArg = new byte[len + 1]; - bufferLib.readIntoByteArray(buffer, 0, ioctlArg, 0, len); + } else { + if (len > IOCTL_BUFSZ) { + throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.IOCTL_STRING_ARG_TOO_LONG); } - try { - int ret = callIoctlBytes(frame, inliningTarget, fd, request, ioctlArg, releaseGil, posixLib, gilNode, constructAndRaiseNode); - if (writable && mutateArg) { - return ret; - } else { - return PFactory.createBytes(context.getLanguage(inliningTarget), ioctlArg, len); - } - } finally { - if (writeBack) { - bufferLib.writeFromByteArray(buffer, 0, ioctlArg, 0, len); - } + } + if (ioctlArg == null) { + ioctlArg = new byte[len + 1]; + bufferLib.readIntoByteArray(buffer, 0, ioctlArg, 0, len); + } + try { + int ret = callIoctlBytes(frame, inliningTarget, fd, request, ioctlArg, releaseGil, posixLib, gilNode, constructAndRaiseNode); + if (writable && mutateArg) { + return ret; + } else { + return PFactory.createBytes(context.getLanguage(inliningTarget), ioctlArg, len); } } finally { - bufferLib.release(buffer, frame, callData); + if (writeBack) { + bufferLib.writeFromByteArray(buffer, 0, ioctlArg, 0, len); + } } + } finally { + bufferLib.release(buffer, frame, callData); } } - // string arg - TruffleString stringArg = null; - try { - stringArg = castToString.execute(inliningTarget, arg); - } catch (CannotCastException e) { - // ignore - } - if (stringArg != null) { - TruffleString.Encoding utf8 = TruffleString.Encoding.UTF_8; - stringArg = switchEncodingNode.execute(stringArg, utf8); - int len = stringArg.byteLength(utf8); - if (len > IOCTL_BUFSZ) { - throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.IOCTL_STRING_ARG_TOO_LONG); - } - byte[] ioctlArg = new byte[len + 1]; - copyToByteArrayNode.execute(stringArg, 0, ioctlArg, 0, len, utf8); - callIoctlBytes(frame, inliningTarget, fd, request, ioctlArg, true, posixLib, gilNode, constructAndRaiseNode); - return PFactory.createBytes(context.getLanguage(inliningTarget), ioctlArg, len); + } + // string arg + TruffleString stringArg = null; + try { + stringArg = castToString.execute(inliningTarget, arg); + } catch (CannotCastException e) { + // ignore + } + if (stringArg != null) { + TruffleString.Encoding utf8 = TruffleString.Encoding.UTF_8; + stringArg = switchEncodingNode.execute(stringArg, utf8); + int len = stringArg.byteLength(utf8); + if (len > IOCTL_BUFSZ) { + throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.IOCTL_STRING_ARG_TOO_LONG); } - - // int arg - intArg = asIntNode.execute(frame, inliningTarget, arg); - // fall through + byte[] ioctlArg = new byte[len + 1]; + copyToByteArrayNode.execute(stringArg, 0, ioctlArg, 0, len, utf8); + callIoctlBytes(frame, inliningTarget, fd, request, ioctlArg, true, posixLib, gilNode, constructAndRaiseNode); + return PFactory.createBytes(context.getLanguage(inliningTarget), ioctlArg, len); } - // default arg or int arg + // int arg + intArg = asIntNode.execute(frame, inliningTarget, arg); + // fall through + } + + // default arg or int arg + try { + gilNode.release(true); try { - gilNode.release(true); - try { - return posixLib.ioctlInt(getPosixSupport(), fd, request, intArg); - } finally { - gilNode.acquire(); - } - } catch (PosixException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); + return posixLib.ioctlInt(getPosixSupport(), fd, request, intArg); + } finally { + gilNode.acquire(); } - } catch (PosixSupportLibrary.UnsupportedPosixFeatureException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorUnsupported(frame, e); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } 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 042ab27c0c..559fea13d7 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 @@ -52,6 +52,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.J___GRAALPYTHON__; import static com.oracle.graal.python.nodes.BuiltinNames.T_FORMAT; import static com.oracle.graal.python.nodes.BuiltinNames.T_MTIME; +import static com.oracle.graal.python.nodes.BuiltinNames.T_PYEXPAT; import static com.oracle.graal.python.nodes.BuiltinNames.T_SHA3; import static com.oracle.graal.python.nodes.BuiltinNames.T_SIZE; import static com.oracle.graal.python.nodes.BuiltinNames.T__IMP; @@ -97,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; @@ -104,13 +106,13 @@ 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.code.CodeNodes; +import com.oracle.graal.python.builtins.objects.cext.copying.NativeLibraryToolException; +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.PCode; import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage; import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; @@ -144,7 +146,6 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.arrow.ArrowArray; import com.oracle.graal.python.nodes.arrow.ArrowSchema; -import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetCallTargetNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.call.CallNode; @@ -164,6 +165,7 @@ import com.oracle.graal.python.nodes.util.CastToJavaStringNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.nodes.util.ToNativePrimitiveStorageNode; +import com.oracle.graal.python.pegparser.InputType; import com.oracle.graal.python.runtime.ExecutionContext; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; import com.oracle.graal.python.runtime.ExecutionContext.InteropCallContext; @@ -236,7 +238,11 @@ public void initialize(Python3Core core) { addBuiltinConstant("is_native", TruffleOptions.AOT); addBuiltinConstant("is_bytecode_dsl_interpreter", PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER); PythonContext ctx = core.getContext(); - TruffleString encodingOpt = ctx.getLanguage().getEngineOption(PythonOptions.StandardStreamEncoding); + PythonLanguage language = ctx.getLanguage(); + // Engine options: if they differ from the values baked into the pre-initialized context, + // this `initialize` method is run again. + addBuiltinConstant("is_forced_uncached_interpreter", language.getEngineOption(PythonOptions.ForceUncachedInterpreter)); + TruffleString encodingOpt = language.getEngineOption(PythonOptions.StandardStreamEncoding); TruffleString standardStreamEncoding = null; TruffleString standardStreamError = null; if (encodingOpt != null && !encodingOpt.isEmpty()) { @@ -289,6 +295,7 @@ public void postInitialize(Python3Core core) { if (!context.getOption(PythonOptions.EnableDebuggingBuiltins)) { mod.setAttribute(tsLiteral("dump_truffle_ast"), PNone.NO_VALUE); + mod.setAttribute(tsLiteral("compiler_bailout_for_tests"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("tdebug"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("set_storage_strategy"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("get_storage_strategy"), PNone.NO_VALUE); @@ -297,13 +304,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); } @@ -394,7 +402,8 @@ private void runFile(PythonContext context, TruffleString inputFilePath) { TruffleFile file = context.getPublicTruffleFileRelaxed(inputFilePath); builder = Source.newBuilder(PythonLanguage.ID, file); } - source = builder.mimeType(PythonLanguage.getCompileMimeType(0, 0)).build(); + builder = PythonLanguage.setPythonOptions(builder, InputType.FILE, 0, 0); + source = builder.build(); // TODO we should handle non-IO errors better } catch (IOException e) { ErrorAndMessagePair error = OSErrorEnum.fromException(e, TruffleString.EqualNode.getUncached()); @@ -410,7 +419,9 @@ private void runFile(PythonContext context, TruffleString inputFilePath) { } PythonLanguage language = context.getLanguage(); RootCallTarget callTarget = (RootCallTarget) context.getEnv().parsePublic(source); + PCode code = PFactory.createCode(language, callTarget); Object[] arguments = PArguments.create(); + PArguments.setCodeObject(arguments, code); PythonModule mainModule = context.getMainModule(); PDict mainDict = GetOrCreateDictNode.executeUncached(mainModule); PArguments.setGlobals(arguments, mainDict); @@ -502,7 +513,7 @@ private static Object doLoadBytecodeFile(Object bytecodePath, Object sourcePath, try { // get_data TruffleString strBytecodePath = PyObjectStrAsTruffleStringNode.executeUncached(bytecodePath); - TruffleFile bytecodeFile = context.getEnv().getPublicTruffleFile(strBytecodePath.toJavaStringUncached()); + TruffleFile bytecodeFile = context.getPublicTruffleFileRelaxed(strBytecodePath); byte[] bytes = bytecodeFile.readAllBytes(); // _classify_pyc if (bytes.length < 16 || !Arrays.equals(bytes, 0, 4, MAGIC_NUMBER_BYTES, 0, 4)) { @@ -552,10 +563,22 @@ private static Object doLoadBytecodeFile(Object bytecodePath, Object sourcePath, cacheKey = ARRAY_ACCESSOR_LE.getLong(bytes, 8); } if (context.getOption(PythonOptions.VerboseFlag)) { - Object message = PyObjectCallMethodObjArgs.executeUncached(MESSAGE, T_FORMAT, bytecodePath, sourcePath); - CallNode.executeUncached(context.lookupBuiltinModule(T__BOOTSTRAP).getAttribute(T__VERBOSE_MESSAGE), message); + PythonModule bootstrap = context.lookupBuiltinModule(T__BOOTSTRAP); + if (bootstrap != null) { + Object message = PyObjectCallMethodObjArgs.executeUncached(MESSAGE, T_FORMAT, bytecodePath, sourcePath); + CallNode.executeUncached(bootstrap.getAttribute(T__VERBOSE_MESSAGE), message); + } + } + TruffleFile sourceFile = null; + if (sourcePath != PNone.NONE) { + try { + TruffleString strSourcePath = PyObjectStrAsTruffleStringNode.executeUncached(sourcePath); + sourceFile = context.getPublicTruffleFileRelaxed(strSourcePath); + } catch (SecurityException | UnsupportedOperationException | IllegalArgumentException ignored) { + // Fall back to Marshal's empty source. + } } - return MarshalModuleBuiltins.fromBytecodeFile(context, bytecodeFile, bytes, 16, bytes.length - 16, cacheKey); + return MarshalModuleBuiltins.fromBytecodeFile(context.getLanguage(), bytecodeFile, sourceFile, bytes, 16, bytes.length - 16, cacheKey); } catch (MarshalModuleBuiltins.Marshal.MarshalError me) { throw PRaiseNode.raiseStatic(inliningTarget, me.type, me.message, me.arguments); } catch (IOException | SecurityException | UnsupportedOperationException | IllegalArgumentException e) { @@ -592,37 +615,27 @@ PBytes doString(VirtualFrame frame, Object filenameObj, public abstract static class DumpTruffleAstNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - public TruffleString doIt(PFunction func) { - return toTruffleStringUncached(NodeUtil.printTreeToString(GetCallTargetNode.getUncached().execute(func).getRootNode())); - } - - @Specialization(guards = "isFunction(method.getFunction())") - @TruffleBoundary - public TruffleString doIt(PMethod method) { - return toTruffleStringUncached(NodeUtil.printTreeToString(GetCallTargetNode.getUncached().execute(method).getRootNode())); - } - - @Specialization - @TruffleBoundary - public TruffleString doIt(PGenerator gen) { - return toTruffleStringUncached(NodeUtil.printTreeToString(gen.getCurrentCallTarget().getRootNode())); - } - - @Specialization - @TruffleBoundary - public TruffleString doIt(PCode code) { - return toTruffleStringUncached(NodeUtil.printTreeToString(CodeNodes.GetCodeRootNode.executeUncached(code))); - } - - @Fallback - @TruffleBoundary - public TruffleString doit(Object object) { + public TruffleString doIt(Object object) { + if (object instanceof PFunction func) { + return toTruffleStringUncached(NodeUtil.printTreeToString(func.getCode().getRootNode())); + } + if (object instanceof PBuiltinFunction func) { + return toTruffleStringUncached(NodeUtil.printTreeToString(func.getFunctionRootNode())); + } + if (object instanceof PMethod method) { + return doIt(method.getFunction()); + } + if (object instanceof PBuiltinMethod method) { + return doIt(method.getBuiltinFunction()); + } + if (object instanceof PGenerator gen) { + return toTruffleStringUncached(NodeUtil.printTreeToString(gen.getCurrentCallTarget().getRootNode())); + } + if (object instanceof PCode code) { + return toTruffleStringUncached(NodeUtil.printTreeToString(code.getRootNode())); + } return toTruffleStringUncached("truffle ast dump not supported for " + object.toString()); } - - protected static boolean isFunction(Object callee) { - return callee instanceof PFunction; - } } @Builtin(name = "current_import", minNumOfPositionalArgs = 0) @@ -644,16 +657,25 @@ Object doIt(Object value) { } } + @Builtin(name = "compiler_bailout_for_tests", minNumOfPositionalArgs = 0) + @GenerateNodeFactory + public abstract static class CompilerBailoutForTests extends PythonBuiltinNode { + @Specialization + Object doIt() { + CompilerDirectives.bailout("test bailout requested by compiler_bailout_for_tests"); + return PNone.NONE; + } + } + @Builtin(name = "tdebug", takesVarArgs = true) @GenerateNodeFactory 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]); } @@ -661,6 +683,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); @@ -793,7 +820,7 @@ public Object doIt(VirtualFrame frame, PFunction func, @TruffleBoundary public synchronized PFunction convertToBuiltin(PFunction func) { - RootNode rootNode = CodeNodes.GetCodeRootNode.executeUncached(func.getCode()); + RootNode rootNode = func.getCode().getRootNode(); if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { if (rootNode instanceof PBytecodeDSLRootNode r) { r.setPythonInternal(true); @@ -810,10 +837,8 @@ public synchronized PFunction convertToBuiltin(PFunction func) { @GenerateNodeFactory public abstract static class BuiltinMethodNode extends PythonUnaryBuiltinNode { @Specialization - public Object doIt(PFunction func, - @Bind Node inliningTarget, - @Cached CodeNodes.GetCodeRootNode getRootNode) { - RootNode rootNode = getRootNode.execute(inliningTarget, func.getCode()); + public Object doIt(PFunction func) { + RootNode rootNode = func.getCode().getRootNode(); if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { if (rootNode instanceof PBytecodeDSLRootNode r) { r.setPythonInternal(true); @@ -918,7 +943,7 @@ TruffleString posixModuleBackend( public abstract static class ZlibModuleBackendNode extends PythonBuiltinNode { @Specialization TruffleString zlibModuleBackend() { - return getContext().getNFIZlibSupport().isAvailable() ? T_NATIVE : T_JAVA; + return getContext().getNativeZlibSupport().isAvailable() ? T_NATIVE : T_JAVA; } } @@ -931,6 +956,15 @@ TruffleString sha3ModuleBackend() { } } + @Builtin(name = "pyexpat_module_backend", minNumOfPositionalArgs = 0) + @GenerateNodeFactory + public abstract static class PyExpatModuleBackendNode extends PythonBuiltinNode { + @Specialization + TruffleString pyexpatModuleBackend() { + return getContext().lookupBuiltinModule(T_PYEXPAT) == null ? T_NATIVE : T_JAVA; + } + } + @Builtin(name = "time_millis", minNumOfPositionalArgs = 0, maxNumOfPositionalArgs = 1, doc = "Like time.time() but in milliseconds resolution.") @GenerateNodeFactory public abstract static class TimeMillis extends PythonUnaryBuiltinNode { @@ -1204,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; } } @@ -1219,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; } } @@ -1353,7 +1397,7 @@ static Object replicate(TruffleString venvPath, int count, @Bind PythonContext context) { try { NativeLibraryLocator.replicate(context.getEnv().getPublicTruffleFile(venvPath.toJavaStringUncached()), context, count); - } catch (IOException | InterruptedException e) { + } catch (NativeLibraryToolException e) { throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.ValueError, e); } return PNone.NONE; @@ -1495,16 +1539,27 @@ public abstract static class CreateArrowPyCapsule extends PythonBinaryBuiltinNod static PTuple doCreate(long arrowArrayAddr, long arrowSchemaAddr, @Bind Node inliningTarget, @Cached PythonCextCapsuleBuiltins.PyCapsuleNewNode pyCapsuleNewNode) { - var 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); - - long schemaDestructor = ctx.arrowSupport.getArrowSchemaDestructor(inliningTarget); - var schemaCapsuleName = new CArrayWrappers.CByteArrayWrapper(ArrowSchema.CAPSULE_NAME); - PyCapsule arrowSchemaCapsule = pyCapsuleNewNode.execute(inliningTarget, arrowSchemaAddr, schemaCapsuleName, schemaDestructor); + PythonContext ctx = getContext(inliningTarget); + long arrayDestructor = ctx.arrowSupport.getArrowArrayDestructor(); + long arrayCapsuleNamePointer = ctx.stringToNativeUtf8Bytes(ArrowArray.CAPSULE_NAME, true); + PyCapsule arrowArrayCapsule = pyCapsuleNewNode.execute(inliningTarget, arrowArrayAddr, arrayCapsuleNamePointer, arrayDestructor); + + long schemaDestructor = ctx.arrowSupport.getArrowSchemaDestructor(); + long schemaCapsuleNamePointer = ctx.stringToNativeUtf8Bytes(ArrowSchema.CAPSULE_NAME, true); + PyCapsule arrowSchemaCapsule = pyCapsuleNewNode.execute(inliningTarget, arrowSchemaAddr, schemaCapsuleNamePointer, schemaDestructor); return PFactory.createTuple(ctx.getLanguage(inliningTarget), new Object[]{arrowSchemaCapsule, arrowArrayCapsule}); } } + + @Builtin(name = "load_file", minNumOfPositionalArgs = 1) + @GenerateNodeFactory + public abstract static class LoadFile extends PythonUnaryBuiltinNode { + @TruffleBoundary + @Specialization + static PNone doit(TruffleString name, + @Bind PythonContext context) { + context.getCore().loadFile(name, context.getCoreHomeOrFail()); + return PNone.NONE; + } + } } 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 b9b5feb9c7..f45ad58611 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,15 +46,18 @@ 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__; +import static com.oracle.graal.python.nodes.StringLiterals.J_PY_EXTENSION; import static com.oracle.graal.python.nodes.StringLiterals.T_EXT_PYD; import static com.oracle.graal.python.nodes.StringLiterals.T_EXT_SO; import static com.oracle.graal.python.nodes.StringLiterals.T_NAME; import static com.oracle.graal.python.runtime.exception.PythonErrorType.NotImplementedError; -import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR; +import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR_LE; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.internString; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; @@ -62,9 +65,6 @@ import java.util.List; import java.util.concurrent.locks.ReentrantLock; -import org.bouncycastle.crypto.macs.SipHash; -import org.bouncycastle.crypto.params.KeyParameter; - import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; import com.oracle.graal.python.annotations.ArgumentClinic.ClinicConversion; @@ -89,7 +89,6 @@ import com.oracle.graal.python.builtins.objects.module.PythonFrozenModule; 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.compiler.CodeUnit; import com.oracle.graal.python.lib.PyMemoryViewFromObject; @@ -118,6 +117,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.TruffleSafepoint; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -126,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; @@ -261,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); @@ -275,17 +274,17 @@ 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; } - if (!context.hasCApiContext()) { + if (context.getCApiState() != PythonContext.CApiState.INITIALIZED) { throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.CAPI_NOT_YET_INITIALIZED); } @@ -474,7 +473,7 @@ static Object run(TruffleString name, Object dataObj, Object code = null; try { - code = MarshalModuleBuiltins.Marshal.load(context, bytes, size, 0); + code = MarshalModuleBuiltins.Marshal.load(context.getLanguage(), bytes, size, 0); } catch (MarshalError | NumberFormatException e) { raiseFrozenError(inliningTarget, raiseNode, FROZEN_INVALID, name); } @@ -490,8 +489,7 @@ static Object run(TruffleString name, Object dataObj, info = result.info; raiseFrozenError(inliningTarget, raiseNode, status, name); - RootCallTarget callTarget = createCallTarget(context, info); - return PFactory.createCode(context.getLanguage(), callTarget); + return createCode(context, info); } } } @@ -541,9 +539,10 @@ static Object run(TruffleString name, boolean withData, PMemoryView data = null; + PythonLanguage language = context.getLanguage(); if (withData) { - byte[] bytes = MarshalModuleBuiltins.serializeCodeUnit(inliningTarget, context, info.code); - data = PyMemoryViewFromObject.getUncached().execute(null, PFactory.createBytes(context.getLanguage(inliningTarget), bytes)); + byte[] bytes = MarshalModuleBuiltins.serializeCodeUnit(inliningTarget, language, info.code); + data = PyMemoryViewFromObject.getUncached().execute(null, PFactory.createBytes(language, bytes)); } Object[] returnValues = new Object[]{ @@ -552,7 +551,7 @@ static Object run(TruffleString name, boolean withData, info.origName == null ? PNone.NONE : info.origName }; - return PFactory.createTuple(context.getLanguage(inliningTarget), returnValues); + return PFactory.createTuple(language, returnValues); } } @@ -623,7 +622,7 @@ public static PythonModule importFrozenModuleObject(Node inliningTarget, PConstr } } - RootCallTarget callTarget = createCallTarget(core.getContext(), info); + PCode code = createCode(core.getContext(), info); PythonModule module = globals == null ? PFactory.createPythonModule(name) : globals; if (info.isPackage) { @@ -631,7 +630,7 @@ public static PythonModule importFrozenModuleObject(Node inliningTarget, PConstr WriteAttributeToPythonObjectNode.getUncached().execute(module, T___PATH__, PFactory.createList(core.getLanguage())); } - CallDispatchers.SimpleIndirectInvokeNode.executeUncached(callTarget, PArguments.withGlobals(module)); + CallDispatchers.SimpleIndirectInvokeNode.executeUncached(code.getRootCallTarget(), PArguments.withGlobals(code, module)); Object origName = info.origName == null ? PNone.NONE : info.origName; WriteAttributeToPythonObjectNode.getUncached().execute(module, T___ORIGNAME__, origName); @@ -639,12 +638,43 @@ public static PythonModule importFrozenModuleObject(Node inliningTarget, PConstr return module; } - private static RootCallTarget createCallTarget(PythonContext context, FrozenInfo info) { - return (RootCallTarget) context.getLanguage().cacheCode(new PythonLanguage.CodeCacheKey(info.origName, System.identityHashCode(info.code)), () -> { - String name = PythonLanguage.FROZEN_FILENAME_PREFIX + info.name + PythonLanguage.FROZEN_FILENAME_SUFFIX; - Source source = Source.newBuilder("python", "", name).content(Source.CONTENT_NONE).build(); - return PythonLanguage.callTargetFromBytecode(context, source, info.code); - }); + private static PCode createCode(PythonContext context, FrozenInfo info) { + String moduleName = info.name.toJavaStringUncached(); + String codeName = PythonLanguage.FROZEN_FILENAME_PREFIX + moduleName + PythonLanguage.FROZEN_FILENAME_SUFFIX; + TruffleFile file = null; + String filename = codeName; + try { + String fs = context.getEnv().getFileNameSeparator(); + String basename = context.getStdlibHome() + fs + moduleName.replace(".", fs); + String path = info.isPackage ? basename + fs + "__init__.py" : basename + J_PY_EXTENSION; + file = context.getEnv().getInternalTruffleFile(path); + if (file.isReadable()) { + filename = path; + } + } catch (UnsupportedOperationException | IllegalArgumentException | SecurityException e) { + // Fallthrough + } + TruffleFile originalFile = file; + boolean isInternal = PythonLanguage.shouldMarkSourceInternal(context); + Source source = context.getLanguage().getOrCreateSource((ignored -> { + Source newSource = Source.newBuilder(PythonLanguage.ID, "", codeName) // + .content(Source.CONTENT_NONE) // + .internal(isInternal) // + .mimeType(PythonLanguage.MIME_TYPE).build(); + PythonLanguage language = context.getLanguage(); + if (originalFile != null) { + language.registerOriginalFile(newSource, originalFile); + } + return newSource; + }), codeName); + RootCallTarget callTarget = (RootCallTarget) context.getLanguage().cacheCode( + new PythonLanguage.CodeCacheKey(info.origName, System.identityHashCode(info.code)), + () -> context.getLanguage().callTargetFromBytecode(source, info.code, isInternal)); + /* + * Setting the original filename as the co_filename is a deviance from CPython, but it's + * more user friendly and lets us freeze more modules without it being too visible. + */ + return PFactory.createCode(context.getLanguage(), callTarget, internString(toTruffleStringUncached(filename))); } /* @@ -712,39 +742,88 @@ static PBytes run(long magicNumber, Object sourceBuffer, @TruffleBoundary public static byte[] hashSource(long magicNumber, byte[] bytes, int length) { - SipHash sipHash = new SipHash(1, 3); - byte[] key = new byte[16]; - ARRAY_ACCESSOR.putLong(key, 0, magicNumber); - sipHash.init(new KeyParameter(key)); - sipHash.update(bytes, 0, length); - byte[] out = new byte[sipHash.getMacSize()]; - sipHash.doFinal(out, 0); + long hash = sipHash13(magicNumber, 0, bytes, length); + byte[] out = new byte[Long.BYTES]; + ARRAY_ACCESSOR_LE.putLong(out, 0, hash); return out; } + private static long sipHash13(long k0, long k1, byte[] src, int length) { + long b = ((long) length) << 56; + long v0 = k0 ^ 0x736f6d6570736575L; + long v1 = k1 ^ 0x646f72616e646f6dL; + long v2 = k0 ^ 0x6c7967656e657261L; + long v3 = k1 ^ 0x7465646279746573L; + int offset = 0; + while (length - offset >= Long.BYTES) { + long mi = ARRAY_ACCESSOR_LE.getLong(src, offset); + offset += Long.BYTES; + v3 ^= mi; + long[] state = singleRound(v0, v1, v2, v3); + v0 = state[0]; + v1 = state[1]; + v2 = state[2]; + v3 = state[3]; + v0 ^= mi; + } + long tail = 0; + int remaining = length - offset; + for (int i = 0; i < remaining; i++) { + tail |= ((long) src[offset + i] & 0xffL) << (Byte.SIZE * i); + } + b |= tail; + v3 ^= b; + long[] state = singleRound(v0, v1, v2, v3); + v0 = state[0]; + v1 = state[1]; + v2 = state[2]; + v3 = state[3]; + v0 ^= b; + v2 ^= 0xff; + for (int i = 0; i < 3; i++) { + state = singleRound(v0, v1, v2, v3); + v0 = state[0]; + v1 = state[1]; + v2 = state[2]; + v3 = state[3]; + } + return (v0 ^ v1) ^ (v2 ^ v3); + } + + private static long[] singleRound(long v0, long v1, long v2, long v3) { + v0 += v1; + v2 += v3; + v1 = Long.rotateLeft(v1, 13) ^ v0; + v3 = Long.rotateLeft(v3, 16) ^ v2; + v0 = Long.rotateLeft(v0, 32); + v2 += v1; + v0 += v3; + v1 = Long.rotateLeft(v1, 17) ^ v2; + v3 = Long.rotateLeft(v3, 21) ^ v0; + v2 = Long.rotateLeft(v2, 32); + return new long[]{v0, v1, v2, v3}; + } + @Override protected ArgumentClinicProvider getArgumentClinic() { return ImpModuleBuiltinsClinicProviders.SourceHashNodeClinicProviderGen.INSTANCE; } } - @Builtin(name = "_fix_co_filename", minNumOfPositionalArgs = 2) + @Builtin(name = "_fix_co_filename", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, parameterNames = {"code", "path"}) @GenerateNodeFactory - public abstract static class FixCoFilename extends PythonBinaryBuiltinNode { + @ArgumentClinic(name = "path", conversion = ClinicConversion.TString) + public abstract static class FixCoFilename extends PythonBinaryClinicBuiltinNode { @Specialization @TruffleBoundary - public Object run(PCode code, PString path, - @Bind Node inliningTarget, - @Cached CastToTruffleStringNode castToStringNode) { - code.setFilename(castToStringNode.execute(inliningTarget, path)); + public Object run(PCode code, TruffleString path) { + code.fixCoFilename(path); return PNone.NONE; } - @Specialization - @TruffleBoundary - public Object run(PCode code, TruffleString path) { - code.setFilename(path); - return PNone.NONE; + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return ImpModuleBuiltinsClinicProviders.FixCoFilenameClinicProviderGen.INSTANCE; } } 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..87de8bda5d 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,9 +47,13 @@ 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.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.mmap.PMMap; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.runtime.PosixConstants; @@ -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), + PythonToNativeInternalNode.executeUncached(promoted, false)); }); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java index 4bf3baf235..bde7a6a0cb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java @@ -118,7 +118,6 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; -import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnitAndRoot; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNodeGen; import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode; @@ -199,7 +198,7 @@ static Object doit(VirtualFrame frame, Object value, Object file, int version, Object savedState = BoundaryCallContext.enter(frame, threadState, boundaryCallData); byte[] data; try { - data = Marshal.dump(context, value, version); + data = Marshal.dump(language, value, version); } catch (IOException e) { throw CompilerDirectives.shouldNotReachHere(e); } catch (Marshal.MarshalError me) { @@ -230,7 +229,7 @@ static Object doit(VirtualFrame frame, Object value, int version, PythonContext.PythonThreadState threadState = context.getThreadState(language); Object savedState = BoundaryCallContext.enter(frame, threadState, boundaryCallData); try { - return PFactory.createBytes(language, Marshal.dump(context, value, version)); + return PFactory.createBytes(language, Marshal.dump(language, value, version)); } catch (IOException e) { throw CompilerDirectives.shouldNotReachHere(e); } catch (Marshal.MarshalError me) { @@ -256,12 +255,13 @@ static Object doit(VirtualFrame frame, Object file, @Cached("createCallReadNode()") LookupAndCallBinaryNode callNode, @CachedLibrary(limit = "3") PythonBufferAcquireLibrary bufferLib, @Cached PRaiseNode raiseNode) { + PythonLanguage language = context.getLanguage(inliningTarget); Object buffer = callNode.executeObject(frame, file, 0); if (!bufferLib.hasBuffer(buffer)) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.READ_RETURNED_NOT_BYTES, buffer); } try { - return Marshal.loadFile(context, file); + return Marshal.loadFile(language, file); } catch (NumberFormatException e) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.BAD_MARSHAL_DATA_S, e.getMessage()); } catch (Marshal.MarshalError me) { @@ -290,7 +290,7 @@ static Object doit(VirtualFrame frame, Object buffer, if (!language.isSingleContext()) { cacheKey = language.cacheKeyForBytecode(bytes, length); } - return Marshal.load(context, bytes, length, cacheKey); + return Marshal.load(language, bytes, length, cacheKey); } catch (NumberFormatException e) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.BAD_MARSHAL_DATA_S, e.getMessage()); } catch (Marshal.MarshalError me) { @@ -347,7 +347,6 @@ public static final class Marshal { private static final char TYPE_ARRAY = ']'; // These are constants that show up in the Bytecode DSL interpreter. private static final char TYPE_GRAALPYTHON_DSL_CODE_UNIT = 'D'; - private static final char TYPE_GRAALPYTHON_DSL_CODE_UNIT_AND_ROOT = 'K'; private static final char TYPE_DSL_SOURCE = '$'; private static final char TYPE_DSL_EMPTY_KEYWORDS = 'k'; @@ -394,15 +393,15 @@ public final Throwable fillInStackTrace() { } @TruffleBoundary - static byte[] dump(PythonContext context, Object value, int version) throws IOException, MarshalError { - Marshal outMarshal = new Marshal(context, version, context.getTrue(), context.getFalse()); + static byte[] dump(PythonLanguage language, Object value, int version) throws IOException, MarshalError { + Marshal outMarshal = new Marshal(language, version); outMarshal.writeObject(value); return outMarshal.outData.toByteArray(); } @TruffleBoundary - static Object load(PythonContext context, byte[] ary, int length, long cacheKey) throws NumberFormatException, MarshalError { - Marshal inMarshal = new Marshal(context, ary, length, cacheKey); + static Object load(PythonLanguage language, byte[] ary, int length, long cacheKey) throws NumberFormatException, MarshalError { + Marshal inMarshal = new Marshal(language, ary, length, cacheKey); Object result = inMarshal.readObject(); if (result == null) { throw new MarshalError(PythonBuiltinClassType.TypeError, ErrorMessages.BAD_MARSHAL_DATA_NULL); @@ -411,8 +410,8 @@ static Object load(PythonContext context, byte[] ary, int length, long cacheKey) } @TruffleBoundary - static Object loadFile(PythonContext context, Object file) throws NumberFormatException, MarshalError { - Marshal inMarshal = new Marshal(context, file); + static Object loadFile(PythonLanguage language, Object file) throws NumberFormatException, MarshalError { + Marshal inMarshal = new Marshal(language, file); Object result = inMarshal.readObject(); if (result == null) { throw new MarshalError(PythonBuiltinClassType.TypeError, ErrorMessages.BAD_MARSHAL_DATA_NULL); @@ -466,18 +465,17 @@ public int read(byte[] b, int off, int len) { } } - private final PythonContext context; + private final PythonLanguage language; final HashMap refMap; final ArrayList refList; final ByteArrayOutputStream outData; final DataOutput out; final DataInput in; final int version; - final PInt pyTrue; - final PInt pyFalse; int depth = 0; long cacheKey; TruffleFile bytecodeFile; + SourceReference sourceReference; // Offset of the buffer in parent buffer in nested deserializations int baseOffset; @@ -488,11 +486,9 @@ public int read(byte[] b, int off, int len) { */ Source source = null; - Marshal(PythonContext context, int version, PInt pyTrue, PInt pyFalse) { - this.context = context; + Marshal(PythonLanguage language, int version) { + this.language = language; this.version = version; - this.pyTrue = pyTrue; - this.pyFalse = pyFalse; this.outData = new ByteArrayOutputStream(); this.out = new DataOutputStream(outData); this.refMap = new HashMap<>(); @@ -500,11 +496,9 @@ public int read(byte[] b, int off, int len) { this.refList = null; } - Marshal(PythonContext context, int version, PInt pyTrue, PInt pyFalse, DataOutput out) { - this.context = context; + Marshal(PythonLanguage language, int version, DataOutput out) { + this.language = language; this.version = version; - this.pyTrue = pyTrue; - this.pyFalse = pyFalse; this.outData = null; this.out = out; this.refMap = new HashMap<>(); @@ -512,28 +506,25 @@ public int read(byte[] b, int off, int len) { this.refList = null; } - Marshal(PythonContext context, byte[] in, int length, long cacheKey) { - this(context, SerializationUtils.createByteBufferDataInput(ByteBuffer.wrap(in, 0, length)), null, null, 0); + Marshal(PythonLanguage language, byte[] in, int length, long cacheKey) { + this(language, SerializationUtils.createByteBufferDataInput(ByteBuffer.wrap(in, 0, length)), null, 0); this.cacheKey = cacheKey; } - Marshal(PythonContext context, byte[] in, int length, long cacheKey, TruffleFile bytecodeFile, int baseOffset) { - this(context, SerializationUtils.createByteBufferDataInput(ByteBuffer.wrap(in, 0, length)), null, bytecodeFile, baseOffset); + Marshal(PythonLanguage language, byte[] in, int length, long cacheKey, TruffleFile bytecodeFile, int baseOffset) { + this(language, SerializationUtils.createByteBufferDataInput(ByteBuffer.wrap(in, 0, length)), bytecodeFile, baseOffset); this.cacheKey = cacheKey; } - Marshal(PythonContext context, Object in) { - this(context, new DataInputStream(new FileLikeInputStream(in)), null, null, 0); + Marshal(PythonLanguage language, Object in) { + this(language, new DataInputStream(new FileLikeInputStream(in)), null, 0); } - Marshal(PythonContext context, DataInput in, Source source, TruffleFile bytecodeFile, int baseOffset) { - this.context = context; + Marshal(PythonLanguage language, DataInput in, TruffleFile bytecodeFile, int baseOffset) { + this.language = language; this.in = in; - this.source = source; this.refList = new ArrayList<>(); this.version = -1; - this.pyTrue = null; - this.pyFalse = null; this.outData = null; this.out = null; this.refMap = null; @@ -541,10 +532,6 @@ public int read(byte[] b, int off, int len) { this.baseOffset = baseOffset; } - private PythonLanguage getLanguage() { - return context.getLanguage(); - } - private void writeByte(int v) { try { out.write(v); @@ -783,9 +770,9 @@ private void writeObject(Object v) throws IOException { writeByte(TYPE_STOPITER); } else if (v == PEllipsis.INSTANCE) { writeByte(TYPE_ELLIPSIS); - } else if (v == Boolean.TRUE || v == pyTrue) { + } else if (v == Boolean.TRUE || v instanceof PInt i && i.getPythonClass() == PythonBuiltinClassType.Boolean && i.isOne()) { writeByte(TYPE_TRUE); - } else if (v == Boolean.FALSE || v == pyFalse) { + } else if (v == Boolean.FALSE || v instanceof PInt i && i.getPythonClass() == PythonBuiltinClassType.Boolean && i.isZero()) { writeByte(TYPE_FALSE); } else if (v instanceof Integer) { writeByte(TYPE_INT); @@ -804,16 +791,6 @@ private void writeObject(Object v) throws IOException { } else if (v instanceof BigInteger) { writeByte(TYPE_BIG_INTEGER); writeBigInteger((BigInteger) v); - } else if (v instanceof BytecodeDSLCodeUnitAndRoot unitAndRoot) { - // BytecodeDSLCodeUnit data should be deserialized to BytecodeDSLCodeUnit if we are - // deserializing a constant in the constants array of BytecodeDSLCodeUnit, otherwise - // if we are deserializing a constant operand of MakeFunction operation, we should - // deserialize it to BytecodeDSLCodeUnitAndRoot. - // In the deserializer we would have to pass down some context to know whether to - // deserialize to one or the other. Instead, we write this extra byte that allows us - // to distinguish this locally during deserialization. - writeByte(TYPE_GRAALPYTHON_DSL_CODE_UNIT_AND_ROOT); - writeReferenceOrComplexObject(unitAndRoot.getCodeUnit()); } else { writeReferenceOrComplexObject(v); } @@ -971,7 +948,7 @@ private void writeComplexObject(Object v, int flag) { } } else if (v instanceof Source s) { writeByte(TYPE_DSL_SOURCE | flag); - setSource(s); + writeSource(s); } else { PythonBufferAcquireLibrary acquireLib = PythonBufferAcquireLibrary.getFactory().getUncached(v); if (acquireLib.hasBuffer(v)) { @@ -1104,17 +1081,17 @@ private Object readObject(int type, AddRefAndReturn addRef) throws NumberFormatE case TYPE_BIG_INTEGER: return readBigInteger(); case TYPE_LONG: - return addRef.run(PFactory.createInt(getLanguage(), readBigInteger())); + return addRef.run(PFactory.createInt(language, readBigInteger())); case TYPE_FLOAT: return addRef.run(readDoubleString()); case TYPE_BINARY_FLOAT: return addRef.run(readDouble()); case TYPE_COMPLEX: - return addRef.run(PFactory.createComplex(getLanguage(), readDoubleString(), readDoubleString())); + return addRef.run(PFactory.createComplex(language, readDoubleString(), readDoubleString())); case TYPE_BINARY_COMPLEX: - return addRef.run(PFactory.createComplex(getLanguage(), readDouble(), readDouble())); + return addRef.run(PFactory.createComplex(language, readDouble(), readDouble())); case TYPE_STRING: - return addRef.run(PFactory.createBytes(getLanguage(), readBytes())); + return addRef.run(PFactory.createBytes(language, readBytes())); case TYPE_ASCII_INTERNED: return addRef.run(readAscii(readSize(), true)); case TYPE_ASCII: @@ -1130,24 +1107,24 @@ private Object readObject(int type, AddRefAndReturn addRef) throws NumberFormatE case TYPE_SMALL_TUPLE: int smallTupleSize = readByteSize(); Object[] smallTupleItems = new Object[smallTupleSize]; - Object smallTuple = addRef.run(PFactory.createTuple(getLanguage(), smallTupleItems)); + Object smallTuple = addRef.run(PFactory.createTuple(language, smallTupleItems)); readArray(smallTupleItems); return smallTuple; case TYPE_TUPLE: int tupleSize = readSize(); Object[] tupleItems = new Object[tupleSize]; - Object tuple = addRef.run(PFactory.createTuple(getLanguage(), tupleItems)); + Object tuple = addRef.run(PFactory.createTuple(language, tupleItems)); readArray(tupleItems); return tuple; case TYPE_LIST: int listSize = readSize(); Object[] listItems = new Object[listSize]; - Object list = addRef.run(PFactory.createList(getLanguage(), listItems)); + Object list = addRef.run(PFactory.createList(language, listItems)); readArray(listItems); return list; case TYPE_DICT: HashingStorage store = PDict.createNewStorage(0); - PDict dict = PFactory.createDict(getLanguage(), store); + PDict dict = PFactory.createDict(language, store); addRef.run(dict); while (true) { Object key = readObject(); @@ -1167,9 +1144,9 @@ private Object readObject(int type, AddRefAndReturn addRef) throws NumberFormatE HashingStorage setStore = EconomicMapStorage.create(setSz); PBaseSet set; if (type == TYPE_FROZENSET) { - set = PFactory.createFrozenSet(getLanguage(), setStore); + set = PFactory.createFrozenSet(language, setStore); } else { - set = PFactory.createSet(getLanguage(), setStore); + set = PFactory.createSet(language, setStore); } addRef.run(set); for (int i = 0; i < setSz; i++) { @@ -1187,15 +1164,8 @@ private Object readObject(int type, AddRefAndReturn addRef) throws NumberFormatE return addRef.run(readBytecodeCodeUnit()); case TYPE_GRAALPYTHON_DSL_CODE_UNIT: return addRef.run(readBytecodeDSLCodeUnit()); - case TYPE_GRAALPYTHON_DSL_CODE_UNIT_AND_ROOT: - Object co = readObject(); - if (co instanceof BytecodeDSLCodeUnit dslUnit) { - return new BytecodeDSLCodeUnitAndRoot(dslUnit); - } else { - throw new MarshalError(ValueError, ErrorMessages.BAD_MARSHAL_DATA); - } case TYPE_DSL_SOURCE: - return getSource(); + return addRef.run(readSource()); case TYPE_DSL_EMPTY_KEYWORDS: return PKeyword.EMPTY_KEYWORDS; case TYPE_ARRAY: @@ -1211,6 +1181,18 @@ private void writeString(TruffleString v) { writeBytes(ba.getArray(), ba.getOffset(), ba.getLength()); } + private void writeSource(Source source) { + writeString(TruffleString.fromJavaStringUncached(source.getName(), TS_ENCODING)); + } + + private Source readSource() { + String name = readString(false).toJavaStringUncached(); + if (sourceReference == null) { + sourceReference = new SourceReference(name); + } + return sourceReference.getSource(language); + } + private TruffleString readString(boolean intern) { int sz = readInt(); if (sz == 0) { @@ -1236,12 +1218,12 @@ private void writeShortString(String v) throws IOException { private TruffleString readShortString() { int sz = readByteSize(); byte[] bytes = readNBytes(sz); - return TruffleString.fromByteArrayUncached(bytes, 0, sz, Encoding.ISO_8859_1, false).switchEncodingUncached(TS_ENCODING); + return TruffleString.fromByteArrayWithCompactionUTF32Uncached(bytes, 0, sz, TruffleString.CompactionLevel.S1, false); } private Object readAscii(int sz, boolean intern) { byte[] bytes = readNBytes(sz); - TruffleString value = TruffleString.fromByteArrayUncached(bytes, 0, sz, Encoding.US_ASCII, false).switchEncodingUncached(TS_ENCODING); + TruffleString value = TruffleString.fromByteArrayWithCompactionUTF32Uncached(bytes, 0, sz, TruffleString.CompactionLevel.S1, false); if (intern) { return PythonUtils.internString(value); } else { @@ -1367,24 +1349,6 @@ private Object[] readObjectArray() { return a; } - private void setSource(Source s) { - if (source == null) { - source = s; - } else if (source != s) { - throw CompilerDirectives.shouldNotReachHere("attempted to serialize with multiple Source objects"); - } - } - - private Source getSource() { - if (source != null) { - return source; - } else { - // This should never happen when deserializing a bytecode DSL code unit, but could - // happen if the user tries to deserialize arbitrary bytes. - throw new MarshalError(ValueError, ErrorMessages.BAD_MARSHAL_DATA); - } - } - private CodeUnit readCodeUnit() { int codeUnitType = readByte(); return switch (codeUnitType) { @@ -1478,10 +1442,11 @@ private BytecodeDSLCodeUnit readBytecodeDSLCodeUnit() { int selfIndex = readInt(); int yieldFromGeneratorIndex = readInt(); int instrumentationDataIndex = readInt(); + int maxProfileCEventStackSize = readInt(); - BytecodeSupplier provider = new BytecodeSupplier(serialized, bytecodeFile, bytecodeOffset, bytecodeSize, cacheKey); + BytecodeSupplier provider = new BytecodeSupplier(serialized, bytecodeFile, sourceReference, bytecodeOffset, bytecodeSize, cacheKey); return new BytecodeDSLCodeUnit(name, qualname, argCount, kwOnlyArgCount, positionalOnlyArgCount, flags, names, varnames, cellvars, freevars, cell2arg, constants, - startLine, startColumn, endLine, endColumn, classcellIndex, selfIndex, yieldFromGeneratorIndex, instrumentationDataIndex, provider); + startLine, startColumn, endLine, endColumn, classcellIndex, selfIndex, yieldFromGeneratorIndex, instrumentationDataIndex, maxProfileCEventStackSize, provider); } private void writeCodeUnit(CodeUnit code) throws IOException { @@ -1531,7 +1496,12 @@ private void writeBytecodeCodeUnit(BytecodeCodeUnit code) throws IOException { } private void writeBytecodeDSLCodeUnit(BytecodeDSLCodeUnit code) throws IOException { - byte[] serialized = code.getSerialized(context); + /* + * Nested code units referenced by MakeFunction are stored in co_consts; the + * MakeFunction instruction itself carries only the integer index into this constants + * array. + */ + byte[] serialized = code.getSerialized(language); writeBytes(serialized); writeString(code.name); writeString(code.qualname); @@ -1557,6 +1527,7 @@ private void writeBytecodeDSLCodeUnit(BytecodeDSLCodeUnit code) throws IOExcepti writeInt(code.selfIndex); writeInt(code.yieldFromGeneratorIndex); writeInt(code.instrumentationDataIndex); + writeInt(code.maxProfileCEventStackSize); } private PCode readCode() { @@ -1566,26 +1537,28 @@ private PCode readCode() { int firstLineNo = readInt(); byte[] lnoTab = readBytes(); com.oracle.graal.python.util.Supplier supplier = () -> { - String jFilename = fileName.toJavaStringUncached(); - Source subSource = Source.newBuilder(PythonLanguage.ID, "", jFilename).content(Source.CONTENT_NONE).build(); - return PythonLanguage.callTargetFromBytecode(context, subSource, code); + String jName = code.qualname.toJavaStringUncached(); + Source source = Source.newBuilder(PythonLanguage.ID, "", jName).content(Source.CONTENT_NONE).build(); + PythonLanguage language = this.language; + if (sourceReference != null && sourceReference.sourceFile != null) { + language.registerOriginalFile(source, sourceReference.sourceFile); + } + return language.callTargetFromBytecode(source, code); }; CallTarget callTarget; - if (context.getLanguage().isSingleContext() || cacheKey == 0) { + if (language.isSingleContext() || cacheKey == 0) { callTarget = supplier.get(); } else { - // get a new ID every time we deserialize the same filename in the same context - long fullCacheKey = cacheKey + context.getDeserializationId(fileName); - callTarget = context.getLanguage().cacheCode(new PythonLanguage.CodeCacheKey(fileName, fullCacheKey), supplier); + callTarget = language.cacheCode(new PythonLanguage.CodeCacheKey(fileName, cacheKey), supplier); } - return PFactory.createCode(context.getLanguage(), (RootCallTarget) callTarget, flags, firstLineNo, lnoTab, fileName); + return PFactory.createCode(language, (RootCallTarget) callTarget, flags, firstLineNo, lnoTab, fileName); } } @TruffleBoundary - public static byte[] serializeCodeUnit(Node locationForRaise, PythonContext context, CodeUnit code) { + public static byte[] serializeCodeUnit(Node locationForRaise, PythonLanguage language, CodeUnit code) { try { - Marshal marshal = new Marshal(context, CURRENT_VERSION, null, null); + Marshal marshal = new Marshal(language, CURRENT_VERSION); marshal.writeCodeUnit(code); return marshal.outData.toByteArray(); } catch (IOException e) { @@ -1598,9 +1571,9 @@ public static byte[] serializeCodeUnit(Node locationForRaise, PythonContext cont } @TruffleBoundary - public static CodeUnit deserializeCodeUnit(Node node, PythonContext context, byte[] bytes) { + public static CodeUnit deserializeCodeUnit(Node node, PythonLanguage language, byte[] bytes) { try { - Marshal marshal = new Marshal(context, bytes, bytes.length, 0); + Marshal marshal = new Marshal(language, bytes, bytes.length, 0); return marshal.readCodeUnit(); } catch (Marshal.MarshalError me) { throw PRaiseNode.raiseStatic(node, me.type, me.message, me.arguments); @@ -1609,28 +1582,56 @@ public static CodeUnit deserializeCodeUnit(Node node, PythonContext context, byt } } + private static final class SourceReference { + private String name; + private final TruffleFile sourceFile; + private Source source; + + SourceReference(String name) { + this.name = name; + this.sourceFile = null; + } + + SourceReference(TruffleFile sourceFile) { + this.sourceFile = sourceFile; + } + + synchronized Source getSource(PythonLanguage language) { + if (source == null) { + String sourceName = sourceFile != null ? sourceFile.getPath() : name; + source = Source.newBuilder(PythonLanguage.ID, "", sourceName).content(Source.CONTENT_NONE).build(); + if (sourceFile != null) { + language.registerOriginalFile(source, sourceFile); + } + } + return source; + } + } + public static class BytecodeSupplier extends BytecodeDSLCodeUnit.BytecodeSupplier { private byte[] serialized; // Original file for reparsing private final TruffleFile bytecodeFile; + private final SourceReference sourceReference; // Offset within the bytecode file, points directly at the start of serialized bytecode private final int bytecodeOffset; private final int bytecodeSize; private final long cacheKey; - public BytecodeSupplier(byte[] serialized, TruffleFile bytecodeFile, int bytecodeOffset, int bytecodeSize, long cacheKey) { + public BytecodeSupplier(byte[] serialized, TruffleFile bytecodeFile, SourceReference sourceReference, int bytecodeOffset, int bytecodeSize, long cacheKey) { this.serialized = serialized; this.bytecodeFile = bytecodeFile; + this.sourceReference = sourceReference; this.bytecodeOffset = bytecodeOffset; this.bytecodeSize = bytecodeSize; this.cacheKey = cacheKey; } @Override - public PBytecodeDSLRootNode createRootNode(PythonContext context, Source source) { + public PBytecodeDSLRootNode createRootNode(PythonLanguage language) { BytecodeRootNodes deserialized; try { - deserialized = PBytecodeDSLRootNodeGen.deserialize(context.getLanguage(), BytecodeConfig.WITH_SOURCE, + deserialized = PBytecodeDSLRootNodeGen.deserialize(language, BytecodeConfig.WITH_SOURCE, () -> SerializationUtils.createByteBufferDataInput(ByteBuffer.wrap(getBytecode())), /* * NB: Since a DSL node may reparse multiple times, we cannot reuse @@ -1638,14 +1639,15 @@ public PBytecodeDSLRootNode createRootNode(PythonContext context, Source source) * different buffer). */ (deserializerContext, buffer) -> { - Marshal marshal = new Marshal(PythonContext.get(null), buffer, source, bytecodeFile, bytecodeOffset); + Marshal marshal = new Marshal(language, buffer, bytecodeFile, bytecodeOffset); + marshal.sourceReference = sourceReference; marshal.cacheKey = cacheKey; return marshal.readObject(); }); } catch (IOException e) { throw CompilerDirectives.shouldNotReachHere("Deserialization error."); } - if (bytecodeFile != null && bytecodeOffset >= 0 && cacheKey != 0 && !context.getOption(PythonOptions.KeepBytecodeInMemory)) { + if (bytecodeFile != null && bytecodeOffset >= 0 && cacheKey != 0 && !language.getEngineOption(PythonOptions.KeepBytecodeInMemory)) { // Free the serialized bytecode, we will fetch it from the file if needed again serialized = null; } @@ -1687,25 +1689,26 @@ private byte[] getBytecode() { } @Override - public byte[] createSerializedBytecode(PythonContext context) { + public byte[] createSerializedBytecode(PythonLanguage language) { return getBytecode(); } } public static class PBytecodeDSLSerializer implements BytecodeSerializer { - private final PythonContext pythonContext; + private final PythonLanguage language; - public PBytecodeDSLSerializer(PythonContext pythonContext) { - this.pythonContext = pythonContext; + public PBytecodeDSLSerializer(PythonLanguage language) { + this.language = language; } + @TruffleBoundary public void serialize(SerializerContext context, DataOutput buffer, Object object) throws IOException { /* * NB: Since the deserializer uses a fresh Marshal instance for each object (see below) * we must also do the same here. Otherwise, the encoding may be different (e.g., a * reference for an already-emitted object). */ - new Marshal(pythonContext, CURRENT_VERSION, pythonContext.getTrue(), pythonContext.getFalse(), buffer).writeObject(object); + new Marshal(language, CURRENT_VERSION, buffer).writeObject(object); } } @@ -1718,8 +1721,9 @@ public ReparseError(String message) { } @TruffleBoundary - public static Object fromBytecodeFile(PythonContext context, TruffleFile file, byte[] bytes, int offset, int length, long cacheKey) throws IOException { - MarshalModuleBuiltins.Marshal marshal = new MarshalModuleBuiltins.Marshal(context, bytes, length + offset, cacheKey, file, 0); + public static Object fromBytecodeFile(PythonLanguage language, TruffleFile bytecodeFile, TruffleFile sourceFile, byte[] bytes, int offset, int length, long cacheKey) throws IOException { + MarshalModuleBuiltins.Marshal marshal = new MarshalModuleBuiltins.Marshal(language, bytes, length + offset, cacheKey, bytecodeFile, 0); + marshal.sourceReference = sourceFile == null ? null : new SourceReference(sourceFile); marshal.in.skipBytes(offset); return marshal.readObject(); } 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/MathModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java index a82c6e2b10..b56a70e834 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathModuleBuiltins.java @@ -72,6 +72,7 @@ import com.oracle.graal.python.nodes.builtins.TupleNodes; import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -841,6 +842,9 @@ static PTuple modfD(double value, } double fraction = value % 1; double integral = value - fraction; + if (integral == 0.0) { + integral = Math.copySign(0.0, value); + } return PFactory.createTuple(language, new Object[]{fraction, integral}); } @@ -2185,11 +2189,11 @@ static Object trunc(VirtualFrame frame, Object obj, @Bind Node inliningTarget, @Cached("create(T___TRUNC__)") LookupAndCallUnaryNode callTrunc, @Cached PRaiseNode raiseNode) { - Object result = callTrunc.executeObject(frame, obj); - if (result == PNone.NO_VALUE) { + try { + return callTrunc.executeObject(frame, obj); + } catch (SpecialMethodNotFound e) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_METHOD, obj, "__trunc__"); } - return result; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MsvcrtModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MsvcrtModuleBuiltins.java index c83eff63c6..4ef96d0031 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MsvcrtModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MsvcrtModuleBuiltins.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,6 +40,8 @@ */ package com.oracle.graal.python.builtins.modules; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; + import java.util.List; import com.oracle.graal.python.annotations.ArgumentClinic; @@ -62,6 +64,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(defineModule = "msvcrt", os = PythonOS.PLATFORM_WIN32) public final class MsvcrtModuleBuiltins extends PythonBuiltins { @@ -69,6 +72,7 @@ public final class MsvcrtModuleBuiltins extends PythonBuiltins { public static final int LK_LOCK = 1; public static final int LK_NBLCK = 2; public static final int LK_UNLOCK = 3; + private static final TruffleString T_MSVCRT_LOCKING = tsLiteral("msvcrt.locking"); @Override protected List> getNodeFactories() { @@ -95,7 +99,7 @@ Object locking(VirtualFrame frame, int fd, int mode, long nbytes, @Cached SysModuleBuiltins.AuditNode auditNode, @CachedLibrary("getPosixSupport()") PosixSupportLibrary posixLib, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "msvcrt.locking", fd, mode, nbytes); + auditNode.audit(frame, inliningTarget, T_MSVCRT_LOCKING, fd, mode, nbytes); try { posixLib.fcntlLock(getPosixSupport(), fd, mode == LK_NBLCK, mode == LK_LOCK ? 1 : 0, 0, 0, nbytes); } catch (PosixSupportLibrary.PosixException e) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/OverlappedModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/OverlappedModuleBuiltins.java new file mode 100644 index 0000000000..1b8d65f11a --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/OverlappedModuleBuiltins.java @@ -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. + */ +package com.oracle.graal.python.builtins.modules; + +import java.util.List; + +import com.oracle.graal.python.annotations.PythonOS; +import com.oracle.graal.python.builtins.CoreFunctions; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; +import com.oracle.truffle.api.dsl.NodeFactory; + +@CoreFunctions(defineModule = "_overlapped", os = PythonOS.PLATFORM_WIN32) +public final class OverlappedModuleBuiltins extends PythonBuiltins { + @Override + protected List> getNodeFactories() { + return List.of(); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java index aff1a9f536..cea24442b7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java @@ -45,7 +45,6 @@ import static com.oracle.graal.python.nodes.ErrorMessages.INTEROP_TYPE_ALREADY_REGISTERED; import static com.oracle.graal.python.nodes.ErrorMessages.INTEROP_TYPE_NOT_MERGABLE; import static com.oracle.graal.python.nodes.ErrorMessages.S_ARG_MUST_BE_S_NOT_P; -import static com.oracle.graal.python.nodes.ErrorMessages.S_CANNOT_HAVE_S; import static com.oracle.graal.python.nodes.ErrorMessages.S_DOES_NOT_TAKE_VARARGS; import static com.oracle.graal.python.nodes.ErrorMessages.S_TAKES_EXACTLY_D_ARGS; import static com.oracle.graal.python.nodes.ErrorMessages.S_TAKES_NO_KEYWORD_ARGS; @@ -706,10 +705,6 @@ void handleArg(Object value, InteropBehaviorMethod method, InteropBehavior inter // validate the function if (function.getKwDefaults().length != 0) { throw raiseNode.raise(this, ValueError, S_TAKES_NO_KEYWORD_ARGS, method.name); - } else if (function.getCode().getCellVars().length != 0) { - throw raiseNode.raise(this, ValueError, S_CANNOT_HAVE_S, method.name, "cell vars"); - } else if (function.getCode().getFreeVars().length != 0) { - throw raiseNode.raise(this, ValueError, S_CANNOT_HAVE_S, method.name, "free vars"); } else { // check signature if (method.takesVarArgs != signature.takesVarArgs()) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java index 4d2dee0b49..650b0c0431 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. * Copyright (c) 2014, Regents of the University of California * * All rights reserved. @@ -27,6 +27,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.T_ENVIRON; import static com.oracle.graal.python.nodes.BuiltinNames.T_NT; +import static com.oracle.graal.python.nodes.BuiltinNames.T_OPEN; import static com.oracle.graal.python.nodes.BuiltinNames.T_POSIX; import static com.oracle.graal.python.nodes.StringLiterals.T_DOT; import static com.oracle.graal.python.runtime.PosixConstants.AT_FDCWD; @@ -100,6 +101,7 @@ import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -120,9 +122,9 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary; import com.oracle.graal.python.runtime.PosixSupportLibrary.Buffer; import com.oracle.graal.python.runtime.PosixSupportLibrary.OpenPtyResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; -import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.PException; @@ -156,6 +158,23 @@ @CoreFunctions(defineModule = "posix", extendsModule = "nt", isEager = true) public final class PosixModuleBuiltins extends PythonBuiltins { + private static final TruffleString T_OS_PUTENV = tsLiteral("os.putenv"); + private static final TruffleString T_OS_UNSETENV = tsLiteral("os.unsetenv"); + private static final TruffleString T_OS_EXEC = tsLiteral("os.exec"); + private static final TruffleString T_OS_TRUNCATE = tsLiteral("os.truncate"); + private static final TruffleString T_OS_REMOVE = tsLiteral("os.remove"); + private static final TruffleString T_OS_MKDIR = tsLiteral("os.mkdir"); + private static final TruffleString T_OS_RMDIR = tsLiteral("os.rmdir"); + private static final TruffleString T_OS_SCANDIR = tsLiteral("os.scandir"); + private static final TruffleString T_OS_LISTDIR = tsLiteral("os.listdir"); + private static final TruffleString T_OS_UTIME = tsLiteral("os.utime"); + private static final TruffleString T_OS_RENAME = tsLiteral("os.rename"); + private static final TruffleString T_OS_CHMOD = tsLiteral("os.chmod"); + private static final TruffleString T_OS_CHOWN = tsLiteral("os.chown"); + private static final TruffleString T_OS_SYSTEM = tsLiteral("os.system"); + private static final TruffleString T_KILL = tsLiteral("kill"); + private static final TruffleString T_KILLPG = tsLiteral("killpg"); + static final StructSequence.BuiltinTypeDescriptor STAT_RESULT_DESC = new StructSequence.BuiltinTypeDescriptor( PythonBuiltinClassType.PStatResult, 10, @@ -411,7 +430,7 @@ static PNone putenv(VirtualFrame frame, PBytes nameBytes, PBytes valueBytes, Object nameOpaque = checkNull(inliningTarget, posixLib.createPathFromBytes(posixSupport, name), raiseNode); Object valueOpaque = checkNull(inliningTarget, posixLib.createPathFromBytes(posixSupport, value), raiseNode); checkEqualSign(inliningTarget, name, raiseNode); - auditNode.audit(inliningTarget, "os.putenv", nameBytes, valueBytes); + auditNode.audit(frame, inliningTarget, T_OS_PUTENV, nameBytes, valueBytes); try { posixLib.setenv(posixSupport, nameOpaque, valueOpaque, true); } catch (PosixException e) { @@ -457,7 +476,7 @@ static PNone putenv(VirtualFrame frame, PBytes nameBytes, @Cached PRaiseNode raiseNode) { byte[] name = toBytesNode.execute(nameBytes); Object nameOpaque = checkNull(inliningTarget, posixLib.createPathFromBytes(context.getPosixSupport(), name), raiseNode); - auditNode.audit(inliningTarget, "os.unsetenv", nameBytes); + auditNode.audit(frame, inliningTarget, T_OS_UNSETENV, nameBytes); try { posixLib.unsetenv(context.getPosixSupport(), nameOpaque); } catch (PosixException e) { @@ -541,7 +560,7 @@ private static void execv(VirtualFrame frame, PosixPath path, Object argv, Seque } // TODO ValueError "execv() arg 2 first element cannot be empty" - auditNode.audit(inliningTarget, "os.exec", path.originalObject, argv, PNone.NONE); + auditNode.audit(frame, inliningTarget, T_OS_EXEC, path.originalObject, argv, PNone.NONE); gil.release(true); try { @@ -580,9 +599,15 @@ static long getUid(@Bind PythonContext context, @GenerateNodeFactory public abstract static class GetEUidNode extends PythonBuiltinNode { @Specialization - static long getUid(@Bind PythonContext context, - @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib) { - return posixLib.geteuid(context.getPosixSupport()); + static long getUid(@Bind Node inliningTarget, + @Bind PythonContext context, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + try { + return posixLib.geteuid(context.getPosixSupport()); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(null, e); + } } } @@ -600,9 +625,15 @@ static long getGid(@Bind PythonContext context, @GenerateNodeFactory public abstract static class GetEGidNode extends PythonBuiltinNode { @Specialization - static long getGid(@Bind PythonContext context, - @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib) { - return posixLib.getegid(context.getPosixSupport()); + static long getGid(@Bind Node inliningTarget, + @Bind PythonContext context, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + try { + return posixLib.getegid(context.getPosixSupport()); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(null, e); + } } } @@ -610,9 +641,15 @@ static long getGid(@Bind PythonContext context, @GenerateNodeFactory public abstract static class GetPpidNode extends PythonBuiltinNode { @Specialization - static long getPpid(@Bind PythonContext context, - @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib) { - return posixLib.getppid(context.getPosixSupport()); + static long getPpid(@Bind Node inliningTarget, + @Bind PythonContext context, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + try { + return posixLib.getppid(context.getPosixSupport()); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(null, e); + } } } @@ -713,9 +750,15 @@ static Object getPpid(VirtualFrame frame, @GenerateNodeFactory public abstract static class GetPgrpNode extends PythonBuiltinNode { @Specialization - static long getPpid(@Bind PythonContext context, - @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib) { - return posixLib.getpgrp(context.getPosixSupport()); + static long getPpid(@Bind Node inliningTarget, + @Bind PythonContext context, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + try { + return posixLib.getpgrp(context.getPosixSupport()); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(null, e); + } } } @@ -826,7 +869,7 @@ static int open(VirtualFrame frame, PosixPath path, int flags, int mode, int dir if (O_CLOEXEC.defined) { fixedFlags |= O_CLOEXEC.getValueIfDefined(); } - auditNode.audit(inliningTarget, "open", path.originalObject, PNone.NONE, fixedFlags); + auditNode.audit(frame, inliningTarget, T_OPEN, path.originalObject, PNone.NONE, fixedFlags); gil.release(true); try { while (true) { @@ -834,7 +877,7 @@ static int open(VirtualFrame frame, PosixPath path, int flags, int mode, int dir return posixLib.openat(context.getPosixSupport(), dirFd, path.value, fixedFlags, mode); } catch (PosixException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { gil.acquire(); // need GIL to construct OSError @@ -930,9 +973,9 @@ public static PBytes read(int fd, int length, throw CompilerDirectives.shouldNotReachHere("Posix read() returned more bytes than requested"); } return PFactory.createBytes(PythonLanguage.get(inliningTarget), result.data, (int) result.length); - } catch (PosixException e) { + } catch (PosixErrnoException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { throw e; @@ -983,9 +1026,9 @@ public static long write(int fd, byte[] dataBytes, while (true) { try { return posixLib.write(posixSupport, fd, new Buffer(dataBytes, dataLen)); - } catch (PosixException e) { + } catch (PosixErrnoException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { throw e; @@ -1191,7 +1234,7 @@ static PNone ftruncate(VirtualFrame frame, int fd, long length, @Cached GilNode gil, @Cached InlinedBranchProfile errorProfile, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.truncate", fd, length); + auditNode.audit(frame, inliningTarget, T_OS_TRUNCATE, fd, length); while (true) { try { gil.release(true); @@ -1203,7 +1246,7 @@ static PNone ftruncate(VirtualFrame frame, int fd, long length, return PNone.NONE; } catch (PosixException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); @@ -1232,7 +1275,7 @@ static PNone truncate(VirtualFrame frame, PosixPath path, long length, @Exclusive @Cached SysModuleBuiltins.AuditNode auditNode, @Exclusive @Cached GilNode gil, @Exclusive @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.truncate", path.originalObject, length); + auditNode.audit(frame, inliningTarget, T_OS_TRUNCATE, path.originalObject, length); try { gil.release(true); try { @@ -1282,7 +1325,7 @@ static PNone fsync(VirtualFrame frame, int fd, return PNone.NONE; } catch (PosixException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); @@ -1481,7 +1524,7 @@ static PTuple doStatFd(VirtualFrame frame, int fd, return createStatResult(inliningTarget, context.getLanguage(inliningTarget), positiveLongProfile, out); } catch (PosixException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); @@ -1595,7 +1638,7 @@ static PNone unlink(VirtualFrame frame, PosixPath path, int dirFd, @Bind Node inliningTarget, @Cached SysModuleBuiltins.AuditNode auditNode, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.remove", path.originalObject, dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_REMOVE, path.originalObject, dirFdForAudit(dirFd)); try { posixLib.unlinkat(context.getPosixSupport(), dirFd, path.value, false); } catch (PosixException e) { @@ -1696,7 +1739,7 @@ static PNone mkdir(VirtualFrame frame, PosixPath path, int mode, int dirFd, @Bind Node inliningTarget, @Cached SysModuleBuiltins.AuditNode auditNode, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.mkdir", path.originalObject, mode, dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_MKDIR, path.originalObject, mode, dirFdForAudit(dirFd)); try { posixLib.mkdirat(context.getPosixSupport(), dirFd, path.value, mode); } catch (PosixException e) { @@ -1724,7 +1767,7 @@ static PNone rmdir(VirtualFrame frame, PosixPath path, int dirFd, @Bind Node inliningTarget, @Cached SysModuleBuiltins.AuditNode auditNode, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.rmdir", path.originalObject, dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_RMDIR, path.originalObject, dirFdForAudit(dirFd)); try { posixLib.unlinkat(context.getPosixSupport(), dirFd, path.value, true); } catch (PosixException e) { @@ -1831,7 +1874,7 @@ static PNone fchdir(VirtualFrame frame, int fd, return PNone.NONE; } catch (PosixException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); @@ -1882,7 +1925,7 @@ static PScandirIterator scandirPath(VirtualFrame frame, PosixPath path, @Bind Node inliningTarget, @Shared @Cached SysModuleBuiltins.AuditNode auditNode, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.scandir", path.originalObject == null ? PNone.NONE : path.originalObject); + auditNode.audit(frame, inliningTarget, T_OS_SCANDIR, path.originalObject == null ? PNone.NONE : path.originalObject); try { return PFactory.createScandirIterator(context.getLanguage(inliningTarget), context, posixLib.opendir(context.getPosixSupport(), path.value), path, false); } catch (PosixException e) { @@ -1897,7 +1940,7 @@ static PScandirIterator scandirFd(VirtualFrame frame, PosixFd fd, @Bind Node inliningTarget, @Shared @Cached SysModuleBuiltins.AuditNode auditNode, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.scandir", fd.originalObject); + auditNode.audit(frame, inliningTarget, T_OS_SCANDIR, fd.originalObject); Object dirStream = dupAndFdopendir(frame, inliningTarget, posixLib, context.getPosixSupport(), fd, constructAndRaiseNode); return PFactory.createScandirIterator(context.getLanguage(inliningTarget), context, dirStream, fd, true); } @@ -1920,7 +1963,7 @@ static PList listdirPath(VirtualFrame frame, PosixPath path, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Shared @Cached SysModuleBuiltins.AuditNode auditNode, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.listdir", path.originalObject == null ? PNone.NONE : path.originalObject); + auditNode.audit(frame, inliningTarget, T_OS_LISTDIR, path.originalObject == null ? PNone.NONE : path.originalObject); try { return listdir(frame, inliningTarget, posixLib.opendir(context.getPosixSupport(), path.value), path.wasBufferLike, false, posixLib, constructAndRaiseNode, context.getLanguage(inliningTarget), context.getPosixSupport()); @@ -1936,7 +1979,7 @@ static PList listdirFd(VirtualFrame frame, PosixFd fd, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Shared @Cached SysModuleBuiltins.AuditNode auditNode, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.listdir", fd.originalObject); + auditNode.audit(frame, inliningTarget, T_OS_LISTDIR, fd.originalObject); Object dirStream = dupAndFdopendir(frame, inliningTarget, posixLib, context.getPosixSupport(), fd, constructAndRaiseNode); return listdir(frame, inliningTarget, dirStream, false, true, posixLib, constructAndRaiseNode, context.getLanguage(inliningTarget), context.getPosixSupport()); } @@ -2102,7 +2145,7 @@ static PNone utimensat(VirtualFrame frame, PosixPath path, Object times, Object @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { long[] timespec = timespecNode.execute(frame, times, ns); - auditNode.audit(inliningTarget, "os.utime", path.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_UTIME, path.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); try { posixLib.utimensat(context.getPosixSupport(), dirFd, path.value, timespec, followSymlinks); } catch (PosixException e) { @@ -2121,7 +2164,7 @@ static PNone utimes(VirtualFrame frame, PosixPath path, Object times, Object ns, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { Timeval[] timeval = timespecNode.toTimeval(frame, times, ns); - auditNode.audit(inliningTarget, "os.utime", path.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_UTIME, path.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); try { posixLib.utimes(context.getPosixSupport(), path.value, timeval); } catch (PosixException e) { @@ -2140,7 +2183,7 @@ static PNone lutimes(VirtualFrame frame, PosixPath path, Object times, Object ns @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { Timeval[] timeval = timespecNode.toTimeval(frame, times, ns); - auditNode.audit(inliningTarget, "os.utime", path.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_UTIME, path.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); try { posixLib.lutimes(context.getPosixSupport(), path.value, timeval); } catch (PosixException e) { @@ -2173,7 +2216,7 @@ static PNone futimens(VirtualFrame frame, PosixFd fd, Object times, Object ns, i @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { long[] timespec = timespecNode.execute(frame, times, ns); - auditNode.audit(inliningTarget, "os.utime", fd.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_UTIME, fd.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); try { posixLib.futimens(context.getPosixSupport(), fd.fd, timespec); } catch (PosixException e) { @@ -2192,7 +2235,7 @@ static PNone futimes(VirtualFrame frame, PosixFd fd, Object times, Object ns, in @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { Timeval[] timeval = timespecNode.toTimeval(frame, times, ns); - auditNode.audit(inliningTarget, "os.utime", fd.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_UTIME, fd.originalObject, checkNone(times), checkNone(ns), dirFdForAudit(dirFd)); try { posixLib.futimes(context.getPosixSupport(), fd.fd, timeval); } catch (PosixException e) { @@ -2241,7 +2284,7 @@ static PNone rename(VirtualFrame frame, PosixPath src, PosixPath dst, int srcDir @Bind Node inliningTarget, @Cached SysModuleBuiltins.AuditNode auditNode, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.rename", src.originalObject, dst.originalObject, dirFdForAudit(srcDirFd), dirFdForAudit(dstDirFd)); + auditNode.audit(frame, inliningTarget, T_OS_RENAME, src.originalObject, dst.originalObject, dirFdForAudit(srcDirFd), dirFdForAudit(dstDirFd)); try { posixLib.renameat(context.getPosixSupport(), srcDirFd, src.value, dstDirFd, dst.value); } catch (PosixException e) { @@ -2285,8 +2328,14 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization static boolean access(PosixPath path, int mode, int dirFd, boolean effectiveIds, boolean followSymlinks, @Bind PythonContext context, - @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib) { - return posixLib.faccessat(context.getPosixSupport(), dirFd, path.value, mode, effectiveIds, followSymlinks); + @Bind Node inliningTarget, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + try { + return posixLib.faccessat(context.getPosixSupport(), dirFd, path.value, mode, effectiveIds, followSymlinks); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(null, e); + } } } @@ -2307,7 +2356,7 @@ static PNone fchmod(VirtualFrame frame, int fd, int mode, @Bind PythonContext context, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.chmod", fd, mode, -1); + auditNode.audit(frame, inliningTarget, T_OS_CHMOD, fd, mode, -1); try { posixLib.fchmod(context.getPosixSupport(), fd, mode); } catch (PosixException e) { @@ -2338,12 +2387,12 @@ static PNone chmodFollow(VirtualFrame frame, PosixPath path, int mode, int dirFd @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Shared @Cached PRaiseNode raiseNode) { - auditNode.audit(inliningTarget, "os.chmod", path.originalObject, mode, dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_CHMOD, path.originalObject, mode, dirFdForAudit(dirFd)); try { posixLib.fchmodat(context.getPosixSupport(), dirFd, path.value, mode, followSymlinks); } catch (PosixException e) { // TODO CPython checks for ENOTSUP as well - if (e.getErrorCode() == OSErrorEnum.EOPNOTSUPP.getNumber() && !followSymlinks) { + if (e.hasErrno(OSErrorEnum.EOPNOTSUPP) && !followSymlinks) { if (dirFd != AT_FDCWD.value) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.CANNOT_USE_FD_AND_FOLLOW_SYMLINKS_TOGETHER, "chmod"); } else { @@ -2364,7 +2413,7 @@ static PNone chmodFollow(VirtualFrame frame, PosixFd fd, int mode, int dirFd, @S @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, // unused node to avoid mixing shared and non-shared inlined nodes @SuppressWarnings("unused") @Shared @Cached PRaiseNode raiseNode) { - auditNode.audit(inliningTarget, "os.chmod", fd.originalObject, mode, dirFdForAudit(dirFd)); + auditNode.audit(frame, inliningTarget, T_OS_CHMOD, fd.originalObject, mode, dirFdForAudit(dirFd)); // Unlike stat and utime which raise CANT_SPECIFY_DIRFD_WITHOUT_PATH or // CANNOT_USE_FD_AND_FOLLOW_SYMLINKS_TOGETHER when an inappropriate combination of // arguments is used, CPython's implementation of chmod simply ignores dir_fd and @@ -2392,7 +2441,7 @@ static Object chown(VirtualFrame frame, int fd, long uid, long gid, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Cached GilNode gil, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.chown", fd, uid, gid, -1); + auditNode.audit(frame, inliningTarget, T_OS_CHOWN, fd, uid, gid, -1); try { gil.release(true); try { @@ -2426,7 +2475,7 @@ static Object chown(VirtualFrame frame, PosixPath path, long uid, long gid, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Cached GilNode gil, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "os.chown", path.originalObject, uid, gid, -1); + auditNode.audit(frame, inliningTarget, T_OS_CHOWN, path.originalObject, uid, gid, -1); try { gil.release(true); try { @@ -2464,7 +2513,7 @@ static Object chown(VirtualFrame frame, PosixPath path, long uid, long gid, int @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, // unused node to avoid mixing shared and non-shared inlined nodes @Shared @Cached PRaiseNode raiseNode) { - auditNode.audit(inliningTarget, "os.chown", path.originalObject, uid, gid, dirFd != AT_FDCWD.value ? dirFd : -1); + auditNode.audit(frame, inliningTarget, T_OS_CHOWN, path.originalObject, uid, gid, dirFd != AT_FDCWD.value ? dirFd : -1); try { gil.release(true); try { @@ -2493,7 +2542,7 @@ static Object chown(VirtualFrame frame, PosixFd fd, long uid, long gid, int dirF if (followSymlinks) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.CANNOT_USE_FD_AND_FOLLOW_SYMLINKS_TOGETHER, "chown"); } - auditNode.audit(inliningTarget, "os.chown", fd.originalObject, uid, gid, -1); + auditNode.audit(frame, inliningTarget, T_OS_CHOWN, fd.originalObject, uid, gid, -1); try { gil.release(true); try { @@ -2629,14 +2678,12 @@ static PTuple waitpid(VirtualFrame frame, long pid, int options, return PFactory.createTuple(context.getLanguage(inliningTarget), new Object[]{result[0], result[1]}); } catch (PosixException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { gil.acquire(); throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } - } catch (UnsupportedPosixFeatureException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorUnsupported(frame, e); } } } finally { @@ -2825,7 +2872,7 @@ protected ArgumentClinicProvider getArgumentClinic() { } @Specialization - static int system(PBytes command, + static int system(VirtualFrame frame, PBytes command, @Bind Node inliningTarget, @Cached BytesNodes.ToBytesNode toBytesNode, @Cached SysModuleBuiltins.AuditNode auditNode, @@ -2835,7 +2882,7 @@ static int system(PBytes command, // Unlike in other posix builtins, we go through str -> bytes -> byte[] -> String // conversions for emulated backend because the bytes version after fsencode conversion // is subject to sys.audit. - auditNode.audit(inliningTarget, "os.system", command); + auditNode.audit(frame, inliningTarget, T_OS_SYSTEM, command); byte[] bytes = toBytesNode.execute(command); gil.release(true); try { @@ -2985,14 +3032,12 @@ static PNone kill(VirtualFrame frame, long pid, int signal, @Bind PythonContext context, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "kill", pid, signal); + auditNode.audit(frame, inliningTarget, T_KILL, pid, signal); try { posixLib.kill(context.getPosixSupport(), pid, signal); return PNone.NONE; } catch (PosixException e) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); - } catch (UnsupportedPosixFeatureException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorUnsupported(frame, e); } } } @@ -3014,14 +3059,12 @@ static PNone kill(VirtualFrame frame, long pid, int signal, @Bind PythonContext context, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "killpg", pid, signal); + auditNode.audit(frame, inliningTarget, T_KILLPG, pid, signal); try { posixLib.killpg(context.getPosixSupport(), pid, signal); return PNone.NONE; } catch (PosixException e) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); - } catch (UnsupportedPosixFeatureException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorUnsupported(frame, e); } } } @@ -3487,8 +3530,10 @@ PosixFileHandle doGeneric(VirtualFrame frame, Object value, @Bind PythonContext context, @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Exclusive @Cached PRaiseNode raiseNode) { - Object pathObject = callFSPath.executeObject(frame, value); - if (pathObject == PNone.NO_VALUE) { + Object pathObject; + try { + pathObject = callFSPath.executeObject(frame, value); + } catch (SpecialMethodNotFound e) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.S_S_SHOULD_BE_S_NOT_P, functionNameWithColon, argumentName, getAllowedTypes(), value); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java index 29d8e2bf57..3092560d74 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.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 @@ -303,7 +303,7 @@ static int forkExec(VirtualFrame frame, Object[] args, Object executableList, bo gil.release(true); try { return posixLib.forkExec(context.getPosixSupport(), executables, processArgs, cwd, env == null ? null : (Object[]) env, stdinRead, stdinWrite, stdoutRead, stdoutWrite, stderrRead, - stderrWrite, errPipeRead, errPipeWrite, closeFds, restoreSignals, callSetsid, fdsToKeep); + stderrWrite, errPipeRead, errPipeWrite, closeFds, restoreSignals, callSetsid, pgidToSet, fdsToKeep, allowVFork); } catch (PosixException e) { gil.acquire(); throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PwdModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PwdModuleBuiltins.java index 3d72ed4ef0..b1f94c8e3e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PwdModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PwdModuleBuiltins.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 @@ -74,7 +74,6 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; @@ -167,8 +166,6 @@ static Object doGetpwuid(VirtualFrame frame, Object uidObj, } } catch (PosixException e) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); - } catch (UnsupportedPosixFeatureException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorUnsupported(frame, e); } if (pwd == null) { throw raiseUidNotFound(inliningTarget, raiseNode); @@ -216,8 +213,6 @@ static Object doGetpwname(VirtualFrame frame, TruffleString name, } } catch (PosixException e) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); - } catch (UnsupportedPosixFeatureException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorUnsupported(frame, e); } if (pwd == null) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.KeyError, ErrorMessages.GETPWNAM_NAME_NOT_FOUND, name); @@ -242,8 +237,6 @@ static Object doGetpall(VirtualFrame frame, entries = posixLib.getpwentries(context.getPosixSupport()); } catch (PosixException e) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); - } catch (UnsupportedPosixFeatureException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorUnsupported(frame, e); } PythonLanguage language = context.getLanguage(inliningTarget); Object[] result = new Object[entries.length]; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ResourceModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ResourceModuleBuiltins.java index 79c3ec20e1..59f59a6a52 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ResourceModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ResourceModuleBuiltins.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 @@ -163,7 +163,7 @@ static PTuple getruusage(VirtualFrame frame, int who, try { rusage = posixLib.getrusage(posixSupport, who); } catch (PosixException e) { - if (e.getErrorCode() == OSErrorEnum.EINVAL.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINVAL)) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.RUSAGE_INVALID_WHO); } else { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SSLModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SSLModuleBuiltins.java index 3d9ec855b6..b5782d298c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SSLModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SSLModuleBuiltins.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,8 +67,6 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; -import org.bouncycastle.util.encoders.DecoderException; - import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; import com.oracle.graal.python.annotations.Builtin; @@ -79,7 +77,6 @@ import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.ssl.CertUtils; -import com.oracle.graal.python.builtins.objects.ssl.LazyBouncyCastleProvider; import com.oracle.graal.python.builtins.objects.ssl.SSLCipher; import com.oracle.graal.python.builtins.objects.ssl.SSLCipherSelector; import com.oracle.graal.python.builtins.objects.ssl.SSLErrorCode; @@ -219,7 +216,6 @@ private static boolean tryProtocolAvailability(SSLContext context, SSLProtocol p @Override public void postInitialize(Python3Core core) { super.postInitialize(core); - LazyBouncyCastleProvider.initProvider(); loadDefaults(core.getContext()); PythonModule module = core.lookupBuiltinModule(T__SSL); module.setAttribute(tsLiteral("OPENSSL_VERSION_NUMBER"), 0); @@ -443,7 +439,7 @@ private Object decode(Node inliningTarget, PConstructAndRaiseNode.Lazy construct throw constructAndRaiseNode.get(inliningTarget).raiseSSLError(null, SSL_ERR_DECODING_PEM_FILE_UNEXPECTED_S, cert.getClass().getName()); } return CertUtils.decodeCertificate(inliningTarget, constructAndRaiseNode, (X509Certificate) certs.get(0), PythonLanguage.get(null)); - } catch (IOException | DecoderException ex) { + } catch (IOException ex) { throw constructAndRaiseNode.get(inliningTarget).raiseSSLError(null, SSL_CANT_OPEN_FILE_S, ex.toString()); } catch (CertificateException | CRLException ex) { throw constructAndRaiseNode.get(inliningTarget).raiseSSLError(null, SSL_ERR_DECODING_PEM_FILE_S, ex.toString()); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SelectModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SelectModuleBuiltins.java index 1691cfe355..abdb923349 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SelectModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SelectModuleBuiltins.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 @@ -155,13 +155,13 @@ static PTuple doGeneric(VirtualFrame frame, Object rlist, Object wlist, Object x } finally { gil.acquire(); } - } catch (PosixException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } catch (ChannelNotSelectableException e) { // GraalPython hack: if one of the channels is not selectable (can happen only in // the emulated mode), we just return everything. notSelectableBranch.enter(inliningTarget); return PFactory.createTuple(language, new Object[]{rlist, wlist, xlist}); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } return PFactory.createTuple(language, new PList[]{ toList(result.getReadFds(), readFDs, language), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SignalModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SignalModuleBuiltins.java index 82c1a457ec..8b5690aa9b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SignalModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SignalModuleBuiltins.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 @@ -51,17 +51,14 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; 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.builtins.CoreFunctions; 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.exception.OSErrorEnum; @@ -77,14 +74,17 @@ 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.PythonQuaternaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.PythonTernaryClinicBuiltinNode; 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.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.runtime.AsyncHandler; import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.PException; @@ -98,6 +98,7 @@ 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.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @@ -109,6 +110,7 @@ public final class SignalModuleBuiltins extends PythonBuiltins { private static final int ITIMER_REAL = 0; private static final int ITIMER_VIRTUAL = 1; private static final int ITIMER_PROF = 2; + private static final TruffleString T_ITIMER_ERROR = tsLiteral("ItimerError"); public static final String J_DEFAULT_INT_HANDLER = "default_int_handler"; public static final TruffleString T_DEFAULT_INT_HANDLER = tsLiteral(J_DEFAULT_INT_HANDLER); @@ -154,6 +156,7 @@ public void postInitialize(Python3Core core) { PythonModule signalModule = core.lookupBuiltinModule(T__SIGNAL); ModuleData moduleData = new ModuleData(); signalModule.setModuleState(moduleData); + signalModule.setAttribute(T_ITIMER_ERROR, core.lookupType(PythonBuiltinClassType.SignalItimerError)); for (int i = 0; i < Signals.PYTHON_SIGNAL_NAMES.length; i++) { TruffleString name = Signals.PYTHON_SIGNAL_NAMES[i]; @@ -188,7 +191,7 @@ public void postInitialize(Python3Core core) { return poll; }); - if (!context.getEnv().isPreInitialization() && context.getOption(PythonOptions.InstallSignalHandlers)) { + if (!context.getEnv().isPreInitialization() && context.getOption(PythonOptions.InstallSignalHandlers) && context.getOption(PythonOptions.AllowSignalHandlers)) { Object defaultSigintHandler = signalModule.getAttribute(T_DEFAULT_INT_HANDLER); assert defaultSigintHandler != PNone.NO_VALUE; SignalNode.signal(null, new Signal("INT").getNumber(), defaultSigintHandler, moduleData); @@ -201,6 +204,21 @@ public static int signalFromName(PythonContext context, String name) { return mod.getModuleState(ModuleData.class).signals.getOrDefault(name, -1); } + @TruffleBoundary + public static void triggerEmulatedSignal(PythonContext context, String name) { + PythonModule mod = context.lookupBuiltinModule(T__SIGNAL); + ModuleData data = mod.getModuleState(ModuleData.class); + if (data == null) { + return; + } + int signum = data.signals.getOrDefault(name, -1); + Object handler = data.signalHandlers.get(signum); + if (handler != null) { + data.signalQueue.add(new SignalTriggerAction(handler, signum)); + data.signalSema.release(); + } + } + public static void resetSignalHandlers(PythonModule mod) { ModuleData data = mod.getModuleState(ModuleData.class); if (data != null) { @@ -266,31 +284,21 @@ static Object validSignals( } } - @Builtin(name = "alarm", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, declaresExplicitSelf = true, parameterNames = {"$mod", "seconds"}) + @Builtin(name = "alarm", minNumOfPositionalArgs = 1, numOfPositionalOnlyArgs = 1, parameterNames = {"seconds"}) @ArgumentClinic(name = "seconds", conversion = ArgumentClinic.ClinicConversion.Int) @GenerateNodeFactory - abstract static class AlarmNode extends PythonBinaryClinicBuiltinNode { + abstract static class AlarmNode extends PythonUnaryClinicBuiltinNode { @Specialization - @TruffleBoundary - int alarm(PythonModule module, int seconds) { - int remaining = 0; - ModuleData data = module.getModuleState(ModuleData.class); - Signals.Alarm currentAlarm = data.currentAlarm; - if (currentAlarm != null) { - if (currentAlarm.isRunning()) { - remaining = currentAlarm.getRemainingSeconds(); - if (remaining < 0) { - remaining = 0; - } - currentAlarm.cancel(); - } - } - if (seconds > 0) { - Signals.Alarm newAlarm = new Signals.Alarm(seconds); - data.currentAlarm = newAlarm; - newAlarm.start(); + static int alarm(VirtualFrame frame, int seconds, + @Bind PythonContext context, + @Bind Node inliningTarget, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + try { + return posixLib.alarm(context.getPosixSupport(), seconds); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } - return remaining; } @Override @@ -303,7 +311,7 @@ protected ArgumentClinicProvider getArgumentClinic() { private static Object handlerToPython(SignalHandler handler, int signum, ModuleData data) { if (!(handler instanceof Signals.PythonSignalHandler)) { // Save default JVM handlers to be restored later - data.defaultSignalHandlers.put(signum, handler); + data.defaultSignalHandlers.putIfAbsent(signum, handler); } if (handler == sun.misc.SignalHandler.SIG_DFL) { return Signals.SIG_DFL; @@ -351,7 +359,12 @@ abstract static class SignalNode extends PythonTernaryBuiltinNode { @Specialization static Object signalHandler(VirtualFrame frame, PythonModule self, Object signal, Object handler, @Bind Node inliningTarget, - @Cached PyNumberAsSizeNode asSizeNode) { + @Bind PythonContext context, + @Cached PyNumberAsSizeNode asSizeNode, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + if (!context.getOption(PythonOptions.AllowSignalHandlers)) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSError(frame, OSErrorEnum.EPERM); + } int signum = asSizeNode.executeExact(frame, inliningTarget, signal); return signalHandlerBoundary(self, handler, inliningTarget, signum); } @@ -448,9 +461,16 @@ protected ArgumentClinicProvider getArgumentClinic() { @GenerateNodeFactory abstract static class RaiseSignalNode extends PythonUnaryClinicBuiltinNode { @Specialization - @TruffleBoundary - static PNone doInt(int signum) { - Signal.raise(new sun.misc.Signal(Signals.signalNumberToName(signum))); + static PNone doInt(VirtualFrame frame, int signum, + @Bind PythonContext context, + @Bind Node inliningTarget, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + try { + posixLib.raise(context.getPosixSupport(), signum); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); + } return PNone.NONE; } @@ -460,10 +480,10 @@ protected ArgumentClinicProvider getArgumentClinic() { } } - @Builtin(name = "setitimer", minNumOfPositionalArgs = 2, declaresExplicitSelf = true, parameterNames = {"$self", "which", "seconds", "interval"}) + @Builtin(name = "setitimer", minNumOfPositionalArgs = 2, parameterNames = {"which", "seconds", "interval"}) @ArgumentClinic(name = "which", conversion = ArgumentClinic.ClinicConversion.Int) @GenerateNodeFactory - abstract static class SetitimerNode extends PythonQuaternaryClinicBuiltinNode { + abstract static class SetitimerNode extends PythonTernaryClinicBuiltinNode { @Override protected ArgumentClinicProvider getArgumentClinic() { @@ -471,68 +491,51 @@ protected ArgumentClinicProvider getArgumentClinic() { } @Specialization - Object doIt(VirtualFrame frame, PythonModule self, int which, Object seconds, Object interval, + static Object doIt(VirtualFrame frame, int which, Object seconds, Object interval, @Bind Node inliningTarget, + @Bind PythonContext context, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Cached PyTimeFromObjectNode timeFromObjectNode, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Bind PythonLanguage language) { - ModuleData moduleData = self.getModuleState(ModuleData.class); - long usDelay = toMicroseconds(frame, inliningTarget, seconds, timeFromObjectNode); - long usInterval = toMicroseconds(frame, inliningTarget, interval, timeFromObjectNode); - if (which != ITIMER_REAL) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSError(frame, OSErrorEnum.EINVAL); + Timeval delay = toTimeval(frame, inliningTarget, seconds, timeFromObjectNode); + Timeval intervalTimeval = toTimeval(frame, inliningTarget, interval, timeFromObjectNode); + try { + return createResultTuple(language, posixLib.setitimer(context.getPosixSupport(), which, delay, intervalTimeval)); + } catch (PosixException e) { + throw raiseItimerError(frame, inliningTarget, e, constructAndRaiseNode); } - PTuple resultTuple = GetitimerNode.createResultTuple(language, moduleData); - setitimer(moduleData, usDelay, usInterval); - return resultTuple; } - private static long toMicroseconds(VirtualFrame frame, Node inliningTarget, Object obj, PyTimeFromObjectNode timeFromObjectNode) { + private static Timeval toTimeval(VirtualFrame frame, Node inliningTarget, Object obj, PyTimeFromObjectNode timeFromObjectNode) { if (obj == PNone.NO_VALUE) { - return 0; + return new Timeval(0, 0); } - return timeFromObjectNode.execute(frame, inliningTarget, obj, RoundType.CEILING, SEC_TO_US); + long microseconds = timeFromObjectNode.execute(frame, inliningTarget, obj, RoundType.CEILING, SEC_TO_US); + return new Timeval(microseconds / SEC_TO_US, microseconds % SEC_TO_US); } - @TruffleBoundary - private void setitimer(ModuleData moduleData, long usDelay, long usInterval) { - if (moduleData.itimerFuture != null) { - moduleData.itimerFuture.cancel(false); - moduleData.itimerFuture = null; - moduleData.itimerInterval = 0; - } - if (usDelay == 0) { - return; - } - moduleData.itimerInterval = usInterval; - Runnable r = () -> Signals.raiseSignal("ALRM"); - ScheduledExecutorService itimerService = getItimerService(moduleData); - if (usInterval == 0) { - moduleData.itimerFuture = itimerService.schedule(r, usDelay, TimeUnit.MICROSECONDS); - } else { - moduleData.itimerFuture = itimerService.scheduleAtFixedRate(r, usDelay, usInterval, TimeUnit.MICROSECONDS); - } + private static PTuple createResultTuple(PythonLanguage language, Timeval[] timeval) { + return PFactory.createTuple(language, new Object[]{toSeconds(timeval[0]), toSeconds(timeval[1])}); } - @TruffleBoundary - private ScheduledExecutorService getItimerService(ModuleData moduleData) { - if (moduleData.itimerService == null) { - ScheduledExecutorService itimerService = Executors.newSingleThreadScheduledExecutor(runnable -> { - Thread t = Executors.defaultThreadFactory().newThread(runnable); - t.setDaemon(true); - return t; - }); - moduleData.itimerService = itimerService; - getContext().registerAtexitHook(ctx -> itimerService.shutdown()); + private static double toSeconds(Timeval timeval) { + return timeval.getSeconds() + timeval.getMicroseconds() / (double) SEC_TO_US; + } + + private static PException raiseItimerError(VirtualFrame frame, Node inliningTarget, PosixException e, PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + if (e instanceof PosixErrnoException errnoException) { + throw constructAndRaiseNode.get(inliningTarget).executeWithArgsOnly(frame, PythonBuiltinClassType.SignalItimerError, + new Object[]{errnoException.getErrorCode(), errnoException.getMessageAsTruffleString()}); } - return moduleData.itimerService; + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } - @Builtin(name = "getitimer", minNumOfPositionalArgs = 1, declaresExplicitSelf = true, parameterNames = {"$self", "which"}) + @Builtin(name = "getitimer", minNumOfPositionalArgs = 1, parameterNames = {"which"}) @ArgumentClinic(name = "which", conversion = ArgumentClinic.ClinicConversion.Int) @GenerateNodeFactory - abstract static class GetitimerNode extends PythonBinaryClinicBuiltinNode { + abstract static class GetitimerNode extends PythonUnaryClinicBuiltinNode { @Override protected ArgumentClinicProvider getArgumentClinic() { @@ -540,33 +543,17 @@ protected ArgumentClinicProvider getArgumentClinic() { } @Specialization - static Object doIt(VirtualFrame frame, PythonModule self, int which, + static Object doIt(VirtualFrame frame, int which, @Bind Node inliningTarget, + @Bind PythonContext context, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Bind PythonLanguage language) { - ModuleData moduleData = self.getModuleState(ModuleData.class); - if (which != ITIMER_REAL) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSError(frame, OSErrorEnum.EINVAL); - } - return createResultTuple(language, moduleData); - } - - static PTuple createResultTuple(PythonLanguage language, ModuleData moduleData) { - long oldInterval = moduleData.itimerInterval; - long oldDelay = getOldDelay(moduleData); - return PFactory.createTuple(language, new Object[]{oldDelay / (double) SEC_TO_US, oldInterval / (double) SEC_TO_US}); - } - - @TruffleBoundary - static long getOldDelay(ModuleData moduleData) { - if (moduleData.itimerFuture == null) { - return 0; - } - long delay = moduleData.itimerFuture.getDelay(TimeUnit.MICROSECONDS); - if (delay < 0) { - return 0; + try { + return SetitimerNode.createResultTuple(language, posixLib.getitimer(context.getPosixSupport(), which)); + } catch (PosixException e) { + throw SetitimerNode.raiseItimerError(frame, inliningTarget, e, constructAndRaiseNode); } - return delay; } } @@ -576,10 +563,6 @@ private static final class ModuleData { final ConcurrentHashMap defaultSignalHandlers = new ConcurrentHashMap<>(); final ConcurrentLinkedDeque signalQueue = new ConcurrentLinkedDeque<>(); final Semaphore signalSema = new Semaphore(0); - ScheduledExecutorService itimerService; - ScheduledFuture itimerFuture; - long itimerInterval; - Signals.Alarm currentAlarm; } } @@ -614,46 +597,6 @@ final class Signals { } } - static final class Alarm { - final int seconds; - final long startMillis; - private Thread thread; - - public Alarm(int seconds) { - this.seconds = seconds; - startMillis = System.currentTimeMillis(); - } - - public void start() { - thread = new Thread(() -> { - try { - Thread.sleep((long) seconds * 1000); - sun.misc.Signal.raise(new sun.misc.Signal("ALRM")); - } catch (InterruptedException e) { - // Cancelled - } - }); - thread.start(); - } - - public boolean isRunning() { - return thread.isAlive(); - } - - public void cancel() { - thread.interrupt(); - } - - public int getRemainingSeconds() { - return seconds - (int) ((System.currentTimeMillis() - startMillis) / 1000); - } - } - - @TruffleBoundary - static void raiseSignal(String name) { - sun.misc.Signal.raise(new sun.misc.Signal(name)); - } - static class PythonSignalHandler implements sun.misc.SignalHandler { private final Runnable handler; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SocketModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SocketModuleBuiltins.java index 16980ff22e..c2a3bdf724 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SocketModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SocketModuleBuiltins.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 @@ -138,6 +138,13 @@ @CoreFunctions(defineModule = J__SOCKET) public final class SocketModuleBuiltins extends PythonBuiltins { + private static final TruffleString T_SOCKET_GETHOSTNAME = tsLiteral("socket.gethostname"); + private static final TruffleString T_SOCKET_GETHOSTBYADDR = tsLiteral("socket.gethostbyaddr"); + private static final TruffleString T_SOCKET_GETHOSTBYNAME = tsLiteral("socket.gethostbyname"); + private static final TruffleString T_SOCKET_GETSERVBYNAME = tsLiteral("socket.getservbyname"); + private static final TruffleString T_SOCKET_GETSERVBYPORT = tsLiteral("socket.getservbyport"); + private static final TruffleString T_SOCKET_GETNAMEINFO = tsLiteral("socket.getnameinfo"); + private static final TruffleString T_SOCKET_GETADDRINFO = tsLiteral("socket.getaddrinfo"); @Override protected List> getNodeFactories() { @@ -239,7 +246,7 @@ static TruffleString doGeneric(VirtualFrame frame, @Cached SysModuleBuiltins.AuditNode auditNode, @Cached GilNode gil, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { - auditNode.audit(inliningTarget, "socket.gethostname"); + auditNode.audit(frame, inliningTarget, T_SOCKET_GETHOSTNAME); try { gil.release(true); try { @@ -274,7 +281,7 @@ static Object doGeneric(VirtualFrame frame, Object ip, * TODO this uses getnameinfo and getaddrinfo to emulate the legacy gethostbyaddr. We * might want to use the legacy API in the future */ - auditNode.audit(inliningTarget, "socket.gethostbyaddr", ip); + auditNode.audit(frame, inliningTarget, T_SOCKET_GETHOSTBYADDR, ip); UniversalSockAddr addr = setIpAddrNode.execute(frame, idnaConverter.execute(frame, ip), AF_UNSPEC.value); int family = sockAddrLibrary.getFamily(addr); try { @@ -308,6 +315,8 @@ static Object doGeneric(VirtualFrame frame, Object ip, } catch (GetAddrInfoException e) { // TODO convert error code from gaierror to herror throw constructAndRaiseNode.get(inliningTarget).executeWithArgsOnly(frame, SocketHError, new Object[]{1, e.getMessageAsTruffleString()}); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } @@ -331,7 +340,7 @@ static TruffleString getHostByName(VirtualFrame frame, Object nameObj, @Cached SocketNodes.SetIpAddrNode setIpAddrNode, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { byte[] name = idnaConverter.execute(frame, nameObj); - auditNode.audit(inliningTarget, "socket.gethostbyname", PFactory.createTuple(context.getLanguage(inliningTarget), new Object[]{nameObj})); + auditNode.audit(frame, inliningTarget, T_SOCKET_GETHOSTBYNAME, PFactory.createTuple(context.getLanguage(inliningTarget), new Object[]{nameObj})); UniversalSockAddr addr = setIpAddrNode.execute(frame, name, AF_INET.value); Inet4SockAddr inet4SockAddr = addrLib.asInet4SockAddr(addr); try { @@ -362,7 +371,7 @@ static Object get(VirtualFrame frame, Object nameObj, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { byte[] name = idnaConverter.execute(frame, nameObj); // The event name is really without the _ex, it's not a copy-paste error - auditNode.audit(inliningTarget, "socket.gethostbyname", PFactory.createTuple(context.getLanguage(inliningTarget), new Object[]{nameObj})); + auditNode.audit(frame, inliningTarget, T_SOCKET_GETHOSTBYNAME, PFactory.createTuple(context.getLanguage(inliningTarget), new Object[]{nameObj})); /* * TODO this uses getaddrinfo to emulate the legacy gethostbyname. It doesn't support * aliases and multiple addresses. We might want to use the legacy gethostbyname_r API @@ -385,7 +394,7 @@ static Object get(VirtualFrame frame, Object nameObj, addrInfoCursorLib.release(cursor); } } catch (GetAddrInfoException e) { - throw constructAndRaiseNode.get(inliningTarget).executeWithArgsOnly(frame, SocketHError, new Object[]{e.getMessageAsTruffleString()}); + throw constructAndRaiseNode.get(inliningTarget).executeWithArgsOnly(frame, SocketGAIError, new Object[]{e.getErrorCode(), e.getMessageAsTruffleString()}); } catch (PosixException e) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } @@ -403,7 +412,7 @@ protected static IdnaFromStringOrBytesConverterNode createIdnaConverter() { @GenerateNodeFactory public abstract static class GetServByNameNode extends PythonBinaryClinicBuiltinNode { @Specialization - static Object getServByName(TruffleString serviceName, Object protocolNameObj, + static Object getServByName(VirtualFrame frame, TruffleString serviceName, Object protocolNameObj, @Bind Node inliningTarget, @Cached InlinedConditionProfile noneProtocol, @Bind PythonContext context, @@ -413,6 +422,7 @@ static Object getServByName(TruffleString serviceName, Object protocolNameObj, @Cached TruffleString.ToJavaStringNode toJavaStringNode, @Cached SysModuleBuiltins.AuditNode auditNode, @Cached GilNode gil, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Cached PRaiseNode raiseNode) { TruffleString protocolName; if (noneProtocol.profile(inliningTarget, PGuards.isNoValue(protocolNameObj))) { @@ -426,7 +436,7 @@ static Object getServByName(TruffleString serviceName, Object protocolNameObj, * TODO this uses getaddrinfo to emulate the legacy getservbyname. We might want to use * the legacy API in the future */ - auditNode.audit(inliningTarget, "socket.getservbyname", serviceName, protocolName != null ? protocolName : ""); + auditNode.audit(frame, inliningTarget, T_SOCKET_GETSERVBYNAME, serviceName, protocolName != null ? protocolName : T_EMPTY_STRING); int protocol = 0; if (protocolName != null) { @@ -450,6 +460,8 @@ static Object getServByName(TruffleString serviceName, Object protocolNameObj, } } catch (GetAddrInfoException e) { throw raiseNode.raise(inliningTarget, OSError, ErrorMessages.SERVICE_PROTO_NOT_FOUND); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } @@ -468,13 +480,14 @@ public abstract static class GetServByPortNode extends PythonBinaryClinicBuiltin public static final TruffleString T_UDP = tsLiteral("udp"); @Specialization - Object getServByPort(int port, Object protocolNameObj, + Object getServByPort(VirtualFrame frame, int port, Object protocolNameObj, @Bind Node inliningTarget, @Cached InlinedConditionProfile nonProtocol, @CachedLibrary(limit = "1") PosixSupportLibrary posixLib, @Cached TruffleString.EqualNode equalNode, @Cached SysModuleBuiltins.AuditNode auditNode, @Cached GilNode gil, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Cached PRaiseNode raiseNode) { TruffleString protocolName; if (nonProtocol.profile(inliningTarget, PGuards.isNoValue(protocolNameObj))) { @@ -491,7 +504,7 @@ Object getServByPort(int port, Object protocolNameObj, if (port < 0 || port > 0xffff) { throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.S_PORT_RANGE, "getservbyport"); } - auditNode.audit(inliningTarget, "socket.getservbyport", port, protocolName != null ? protocolName : ""); + auditNode.audit(frame, inliningTarget, T_SOCKET_GETSERVBYPORT, port, protocolName != null ? protocolName : T_EMPTY_STRING); try { gil.release(true); @@ -510,6 +523,8 @@ Object getServByPort(int port, Object protocolNameObj, } } catch (GetAddrInfoException e) { throw raiseNode.raise(inliningTarget, OSError, ErrorMessages.SERVICE_PROTO_NOT_FOUND); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } @@ -569,7 +584,7 @@ static Object getNameInfo(VirtualFrame frame, PTuple sockaddr, int flags, scopeid = asIntNode.execute(frame, inliningTarget, getItem.execute(inliningTarget, addr, 3)); } - auditNode.audit(inliningTarget, "socket.getnameinfo", sockaddr); + auditNode.audit(frame, inliningTarget, T_SOCKET_GETNAMEINFO, sockaddr); try { UniversalSockAddr resolvedAddr; @@ -612,6 +627,8 @@ static Object getNameInfo(VirtualFrame frame, PTuple sockaddr, int flags, return PFactory.createTuple(context.getLanguage(inliningTarget), new Object[]{host, service}); } catch (GetAddrInfoException e) { throw constructAndRaiseNode.get(inliningTarget).executeWithArgsOnly(frame, SocketGAIError, new Object[]{e.getErrorCode(), e.getMessageAsTruffleString()}); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } @@ -673,7 +690,7 @@ static Object getAddrInfo(VirtualFrame frame, Object hostObject, Object portObje throw raiseNode.raise(inliningTarget, OSError, ErrorMessages.INT_OR_STRING_EXPECTED); } - auditNode.audit(inliningTarget, "socket.getaddrinfo", hostObject, portObjectProfiled, family, type, proto, flags); + auditNode.audit(frame, inliningTarget, T_SOCKET_GETADDRINFO, hostObject, portObjectProfiled, family, type, proto, flags); AddrInfoCursor cursor; try { @@ -686,6 +703,8 @@ static Object getAddrInfo(VirtualFrame frame, Object hostObject, Object portObje } } catch (GetAddrInfoException e) { throw constructAndRaiseNode.get(inliningTarget).executeWithArgsOnly(frame, SocketGAIError, new Object[]{e.getErrorCode(), e.getMessageAsTruffleString()}); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } try { SequenceStorage storage = new ObjectSequenceStorage(5); @@ -737,7 +756,7 @@ static Object close(VirtualFrame frame, Object fdObj, } } catch (PosixException e) { // CPython ignores ECONNRESET on close - if (e.getErrorCode() != OSErrorEnum.ECONNRESET.getNumber()) { + if (!e.hasErrno(OSErrorEnum.ECONNRESET)) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/StringModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/StringModuleBuiltins.java index f6b2d6a9d6..cb1cd7b2ef 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/StringModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/StringModuleBuiltins.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,6 +44,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.J_FORMATTER_PARSER; import static com.oracle.graal.python.nodes.BuiltinNames.J__STRING; +import java.util.ArrayList; import java.util.List; import com.oracle.graal.python.PythonLanguage; @@ -87,7 +88,7 @@ protected ArgumentClinicProvider getArgumentClinic() { PSequenceIterator formatterParser(VirtualFrame frame, TruffleString self, @Cached("createFor($node)") BoundaryCallData boundaryCallData) { TemplateFormatter formatter = new TemplateFormatter(self); - List parserList; + ArrayList parserList; PythonContext context = PythonContext.get(this); PythonLanguage language = context.getLanguage(this); Object state = ExecutionContext.BoundaryCallContext.enter(frame, language, context, boundaryCallData); @@ -100,7 +101,7 @@ PSequenceIterator formatterParser(VirtualFrame frame, TruffleString self, } } - private static PSequenceIterator parserListToIterator(List parserList, PythonLanguage language) { + private static PSequenceIterator parserListToIterator(ArrayList parserList, PythonLanguage language) { Object[] tuples = new Object[parserList.size()]; for (int i = 0; i < tuples.length; i++) { tuples[i] = PFactory.createTuple(language, parserList.get(i)); 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 fab70a3b20..71d80cccf8 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 @@ -40,7 +40,6 @@ */ package com.oracle.graal.python.builtins.modules; -import static com.oracle.graal.python.PythonLanguage.J_GRAALPYTHON_ID; import static com.oracle.graal.python.PythonLanguage.RELEASE_LEVEL; import static com.oracle.graal.python.PythonLanguage.RELEASE_SERIAL; import static com.oracle.graal.python.PythonLanguage.T_GRAALPYTHON_ID; @@ -61,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; @@ -140,13 +138,21 @@ 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.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; @@ -169,11 +175,13 @@ 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.common.ObjectHashMap; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; import com.oracle.graal.python.builtins.objects.exception.GetEscapedExceptionNode; @@ -207,6 +215,8 @@ import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetAttr; +import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyObjectIsInstanceNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; import com.oracle.graal.python.lib.PyObjectSetAttr; @@ -222,8 +232,10 @@ import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; -import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.NoAttributeHandler; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; +import com.oracle.graal.python.nodes.frame.MaterializeFrameNode; import com.oracle.graal.python.nodes.frame.ReadFrameNode; +import com.oracle.graal.python.nodes.frame.ReadFrameNode.AllPythonFramesSelector; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -232,23 +244,29 @@ import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.nodes.util.ExceptionStateNodes.GetCaughtExceptionNode; import com.oracle.graal.python.runtime.CallerFlags; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; +import com.oracle.graal.python.runtime.GilNode; 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.ThreadLocalAction; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.TruffleSafepoint; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -261,9 +279,9 @@ import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.exception.AbstractTruffleException; +import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(defineModule = "sys", isEager = true) @@ -285,6 +303,7 @@ public final class SysModuleBuiltins extends PythonBuiltins { public static final TruffleString T_CACHE_TAG = tsLiteral("cache_tag"); public static final TruffleString T__MULTIARCH = tsLiteral("_multiarch"); + public static final TruffleString T_ABIFLAGS = tsLiteral("abiflags"); static { String compile_time; @@ -473,10 +492,7 @@ protected List> getNodeFa private static PSimpleNamespace makeImplementation(PythonLanguage language, PTuple graalpyVersionInfo, TruffleString gmultiarch) { final PSimpleNamespace ns = PFactory.createSimpleNamespace(language); ns.setAttribute(StringLiterals.T_NAME, T_GRAALPYTHON_ID); - /*- 'cache_tag' must match the format of mx.graalpython/mx_graalpython.py:graalpy_ext */ - ns.setAttribute(T_CACHE_TAG, toTruffleStringUncached(J_GRAALPYTHON_ID + - PythonLanguage.GRAALVM_MAJOR + PythonLanguage.GRAALVM_MINOR + PythonLanguage.DEV_TAG + - "-" + PythonLanguage.MAJOR + PythonLanguage.MINOR)); + ns.setAttribute(T_CACHE_TAG, toTruffleStringUncached(PythonLanguage.GRAALPY_ABI_VERSION)); ns.setAttribute(T_VERSION, graalpyVersionInfo); ns.setAttribute(T__MULTIARCH, gmultiarch); ns.setAttribute(tsLiteral("hexversion"), PythonLanguage.GRAALVM_MAJOR << 24 | PythonLanguage.GRAALVM_MINOR << 16 | PythonLanguage.GRAALVM_MICRO << 8 | RELEASE_LEVEL << 4 | RELEASE_SERIAL); @@ -497,7 +513,7 @@ public void initialize(Python3Core core) { StructSequence.initType(core, THREAD_INFO_DESC); StructSequence.initType(core, UNRAISABLEHOOK_ARGS_DESC); - addBuiltinConstant("abiflags", T_EMPTY_STRING); + addBuiltinConstant(T_ABIFLAGS, toTruffleStringUncached(PythonLanguage.GRAALPY_ABIFLAGS)); addBuiltinConstant("byteorder", ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? T_LITTLE : T_BIG); addBuiltinConstant("copyright", T_LICENSE); addBuiltinConstant(T_MODULES, PFactory.createDict(language)); @@ -882,22 +898,90 @@ static PFrame counted(VirtualFrame frame, int depth, @Builtin(name = "_current_frames") @GenerateNodeFactory abstract static class CurrentFrames extends PythonBuiltinNode { + private static final long CURRENT_FRAMES_TIMEOUT_MILLIS = 20; + private static final TruffleString T_SYS_CURRENT_FRAMES = tsLiteral("sys._current_frames"); + @Specialization Object currentFrames(VirtualFrame frame, @Bind Node inliningTarget, @Cached AuditNode auditNode, @Cached WarningsModuleBuiltins.WarnNode warnNode, @Cached ReadFrameNode readFrameNode, - @Cached HashingStorageSetItem setHashingStorageItem, + @Cached PyObjectHashNode hashNode, + @Cached ObjectHashMap.PutNode putNode, + @Bind PythonContext context, @Bind PythonLanguage language) { - auditNode.audit(inliningTarget, "sys._current_frames"); - if (!getLanguage().singleThreadedAssumption.isValid()) { - warnNode.warn(frame, RuntimeWarning, ErrorMessages.WARN_CURRENT_FRAMES_MULTITHREADED); - } + auditNode.audit(frame, inliningTarget, T_SYS_CURRENT_FRAMES); PFrame currentFrame = readFrameNode.getCurrentPythonFrame(frame); - PDict result = PFactory.createDict(language); - result.setDictStorage(setHashingStorageItem.execute(frame, inliningTarget, result.getDictStorage(), PThread.getThreadId(Thread.currentThread()), currentFrame)); - return result; + EconomicMapStorage framesMap = collectCurrentFrames(inliningTarget, context, currentFrame); + return PFactory.createDict(language, framesMap); + } + + @TruffleBoundary + @SuppressWarnings("try") + private static EconomicMapStorage collectCurrentFrames(Node inliningTarget, PythonContext context, PFrame currentFrame) { + Thread currentThread = Thread.currentThread(); + Thread[] threads = context.getThreads(); + ArrayList targetThreads = new ArrayList<>(threads.length); + ConcurrentHashMap frames = new ConcurrentHashMap<>(); + frames.put(currentThread, escapedFrameOrPlaceholder(currentFrame)); + for (Thread thread : threads) { + if (thread != currentThread && thread.isAlive()) { + targetThreads.add(thread); + } + } + if (!targetThreads.isEmpty()) { + Thread[] threadArray = targetThreads.toArray(new Thread[0]); + Future future = context.getEnv().submitThreadLocal(threadArray, new ThreadLocalAction(true, false) { + @Override + protected void perform(Access access) { + /* + * Force-escape the frame while we are still running in the target thread. + * Otherwise, the TLA can capture a frame after that frame has already + * checked info.isEscaped() in CalleeContext.exitImpl. Merely marking the + * returned PFrame as escaped would then be too late: the frame can unwind + * without exitEscaped filling in callerInfo, and f_back would be unable to + * recover the caller by stack-walking from the already-unwound frame. + */ + PFrame pyFrame = ReadFrameNode.readFrameInThreadLocal(access, null, FrameAccess.READ_ONLY, AllPythonFramesSelector.INSTANCE, 0, CallerFlags.NEEDS_PFRAME, + MaterializeFrameNode.getUncached(), true); + frames.put(access.getThread(), escapedFrameOrPlaceholder(pyFrame)); + } + }); + boolean[] timedOut = new boolean[1]; + try (var gil = GilNode.uncachedRelease()) { + TruffleSafepoint.setBlockedThreadInterruptible(inliningTarget, voidFuture -> { + try { + voidFuture.get(CURRENT_FRAMES_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + timedOut[0] = true; + } catch (CancellationException e) { + // Ignore cancellation; unanswered threads will get assigned NONE + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + }, future); + } + if (timedOut[0]) { + future.cancel(false); + } + } + + EconomicMapStorage storage = EconomicMapStorage.create(targetThreads.size()); + for (Thread thread : targetThreads) { + long key = PThread.getThreadId(thread); + long hash = PyObjectHashNode.hash(key); + ObjectHashMap.PutNode.putUncached(storage, key, hash, frames.getOrDefault(thread, PNone.NONE)); + } + return storage; + } + + private static Object escapedFrameOrPlaceholder(PFrame frame) { + if (frame != null) { + frame.getRef().markAsEscaped(); + return frame; + } + return PNone.NONE; } } @@ -966,23 +1050,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); + @TruffleBoundary + static long doGeneric(Object object, + @Bind PythonContext context) { + if (!context.isNativeAccessAllowed()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new RuntimeException(ErrorMessages.NATIVE_ACCESS_NOT_ALLOWED.toJavaStringUncached()); } - - PythonAbstractObjectNativeWrapper wrapper = object.getNativeWrapper(); - if (wrapper == null) { - return -1; - } else { - return wrapper.getRefCount(); + if (context.getCApiState() != CApiState.INITIALIZED) { + if (object instanceof PythonAbstractObject pythonAbstractObject) { + return FirstToNativeNode.getInitialRefcnt(false, PythonToNativeInternalNode.isImmortal(context, pythonAbstractObject)); + } + return PythonObject.MANAGED_REFCNT; } - } - - @Fallback - protected long doGeneric(@SuppressWarnings("unused") Object object) { - return -1; + /* + * 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; } } @@ -995,7 +1090,13 @@ static Object doGeneric(VirtualFrame frame, Object object, @SuppressWarnings("un @Shared @Cached PyNumberAsSizeNode asSizeNode, @Cached("createWithError()") LookupAndCallUnaryNode callSizeofNode, @Shared @Cached PRaiseNode raiseNode) { - return checkResult(frame, inliningTarget, asSizeNode, callSizeofNode.executeObject(frame, object), raiseNode); + Object result; + try { + result = callSizeofNode.executeObject(frame, object); + } catch (SpecialMethodNotFound e) { + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_METHOD, object, T___SIZEOF__); + } + return checkResult(frame, inliningTarget, asSizeNode, result, raiseNode); } @Specialization(guards = "!isNoValue(dflt)") @@ -1004,8 +1105,10 @@ static Object doGeneric(VirtualFrame frame, Object object, Object dflt, @Shared @Cached PyNumberAsSizeNode asSizeNode, @Cached("createWithoutError()") LookupAndCallUnaryNode callSizeofNode, @Shared @Cached PRaiseNode raiseNode) { - Object result = callSizeofNode.executeObject(frame, object); - if (result == PNone.NO_VALUE) { + Object result; + try { + result = callSizeofNode.executeObject(frame, object); + } catch (SpecialMethodNotFound e) { return dflt; } return checkResult(frame, inliningTarget, asSizeNode, result, raiseNode); @@ -1021,15 +1124,7 @@ private static Object checkResult(VirtualFrame frame, Node inliningTarget, PyNum @NeverDefault protected LookupAndCallUnaryNode createWithError() { - return LookupAndCallUnaryNode.create(T___SIZEOF__, () -> new NoAttributeHandler() { - private final BranchProfile errorProfile = BranchProfile.create(); - - @Override - public Object execute(Object receiver) { - errorProfile.enter(); - throw PRaiseNode.raiseStatic(this, TypeError, ErrorMessages.TYPE_DOESNT_DEFINE_METHOD, receiver, T___SIZEOF__); - } - }); + return LookupAndCallUnaryNode.create(T___SIZEOF__); } @NeverDefault @@ -1038,31 +1133,32 @@ protected static LookupAndCallUnaryNode createWithoutError() { } } - // TODO implement support for audit events @GenerateUncached @GenerateInline @GenerateCached(false) public abstract static class AuditNode extends Node { - protected abstract void executeInternal(Node inliningTarget, Object event, Object[] arguments); - - public void audit(Node inliningTarget, String event, Object... arguments) { - executeInternal(inliningTarget, event, arguments); - } + protected abstract void executeInternal(VirtualFrame frame, Node inliningTarget, TruffleString event, Object[] arguments); - public static void auditUncached(String event, Object... arguments) { - SysModuleBuiltinsFactory.AuditNodeGen.getUncached().executeInternal(null, event, arguments); + public static void auditUncached(TruffleString event, Object... arguments) { + SysModuleBuiltinsFactory.AuditNodeGen.getUncached().executeInternal(null, null, event, arguments); } - public void audit(Node inliningTarget, TruffleString event, Object... arguments) { - executeInternal(inliningTarget, event, arguments); + public void audit(VirtualFrame frame, Node inliningTarget, TruffleString event, Object... arguments) { + executeInternal(frame, inliningTarget, event, arguments); } @Specialization - void doAudit(@SuppressWarnings("unused") TruffleString event, @SuppressWarnings("unused") Object[] arguments) { - } - - @Specialization - void doAudit(@SuppressWarnings("unused") String event, @SuppressWarnings("unused") Object[] arguments) { + static void doAudit(VirtualFrame frame, Node inliningTarget, TruffleString event, Object[] arguments, + @Bind PythonContext context, + @Bind PythonLanguage language, + @Cached CallNode.Lazy callNode) { + Object[] hooks = context.getAuditHooks(); + if (hooks.length > 0) { + PTuple argsTuple = PFactory.createTuple(language, arguments); + for (Object hook : hooks) { + callNode.get(inliningTarget).execute(frame, hook, event, argsTuple); + } + } } } @@ -1072,9 +1168,18 @@ void doAudit(@SuppressWarnings("unused") String event, @SuppressWarnings("unused @GenerateNodeFactory abstract static class SysAuditNode extends PythonBuiltinNode { @Specialization - @SuppressWarnings("unused") - Object doAudit(VirtualFrame frame, Object event, Object[] args) { - // TODO: Stub audit hooks implementation for PEP 578 + static Object doAudit(VirtualFrame frame, Object event, Object[] args, + @Bind Node inliningTarget, + @Cached CastToTruffleStringNode castEventNode, + @Cached AuditNode auditNode, + @Cached PRaiseNode raiseNode) { + final TruffleString eventString; + try { + eventString = castEventNode.execute(inliningTarget, event); + } catch (CannotCastException e) { + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.S_ARG_1_MUST_BE_STR_NOT_P, "audit", event); + } + auditNode.audit(frame, inliningTarget, eventString, args); return PNone.NONE; } } @@ -1085,10 +1190,21 @@ Object doAudit(VirtualFrame frame, Object event, Object[] args) { "Adds a new audit hook callback.") @GenerateNodeFactory abstract static class SysAuditHookNode extends PythonBuiltinNode { + private static final TruffleString T_SYS_ADDAUDITHOOK = tsLiteral("sys.addaudithook"); + @Specialization - @SuppressWarnings("unused") - Object doAudit(VirtualFrame frame, Object hook) { - // TODO: Stub audit hooks implementation for PEP 578 + static Object doAudit(VirtualFrame frame, Object hook, + @Bind Node inliningTarget, + @Bind PythonContext context, + @Cached AuditNode auditNode, + @Cached IsBuiltinObjectProfile exceptionProfile) { + try { + auditNode.audit(frame, inliningTarget, T_SYS_ADDAUDITHOOK); + } catch (PException pe) { + pe.expect(inliningTarget, PythonBuiltinClassType.Exception, exceptionProfile); + return PNone.NONE; + } + context.addAuditHook(hook); return PNone.NONE; } } @@ -1915,7 +2031,7 @@ Object doHook(VirtualFrame frame, Object[] args, PKeyword[] keywords, @Cached PyObjectGetAttr getAttr, @Cached PyImportImport importNode, @Cached IsBuiltinObjectProfile attrErrorProfile, - @Cached BuiltinFunctions.IsInstanceNode isInstanceNode, + @Cached PyObjectIsInstanceNode isInstanceNode, @Cached WarningsModuleBuiltins.WarnNode warnNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode, @@ -1952,7 +2068,7 @@ Object doHook(VirtualFrame frame, Object[] args, PKeyword[] keywords, try { module = importNode.execute(frame, inliningTarget, modPath); } catch (PException pe) { - if (isInstanceNode.executeWith(frame, pe.getUnreifiedException(), ImportError)) { + if (isInstanceNode.execute(frame, pe.getUnreifiedException(), ImportError)) { warnNode.warnFormat(frame, RuntimeWarning, WARN_IGNORE_UNIMPORTABLE_BREAKPOINT_S, hookName); } return PNone.NONE; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TRegexUtil.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TRegexUtil.java index cdd0441fdc..34edb85a36 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TRegexUtil.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TRegexUtil.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 @@ -91,8 +91,8 @@ private RegexResult() { private static final String NUMBER_OF_REGEX_RESULT_TYPES = "1"; - @GenerateCached - @GenerateInline(inlineByDefault = true) + @GenerateCached(false) + @GenerateInline @GenerateUncached public abstract static class InteropReadMemberNode extends Node { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.java index 82dfdce7dd..22da1f9d4c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ThreadModuleBuiltins.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 @@ -43,6 +43,8 @@ import static com.oracle.graal.python.builtins.objects.thread.AbstractPythonLock.TIMEOUT_MAX; import static com.oracle.graal.python.nodes.BuiltinNames.J_EXIT; import static com.oracle.graal.python.nodes.BuiltinNames.J__THREAD; +import static com.oracle.graal.python.nodes.BuiltinNames.T_STDERR; +import static com.oracle.graal.python.nodes.BuiltinNames.T___EXCEPTHOOK__; import static com.oracle.graal.python.nodes.BuiltinNames.T__THREAD; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; @@ -57,11 +59,20 @@ 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.common.SequenceStorageNodes; +import com.oracle.graal.python.builtins.objects.exception.PBaseException; import com.oracle.graal.python.builtins.objects.function.PKeyword; 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.thread.PLock; import com.oracle.graal.python.builtins.objects.thread.PThread; +import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.builtins.objects.tuple.StructSequence; +import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyNumberAsSizeNode; +import com.oracle.graal.python.lib.PyObjectLookupAttr; +import com.oracle.graal.python.lib.PyObjectSetAttr; +import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.WriteUnraisableNode; @@ -76,11 +87,13 @@ 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.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +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.graal.python.runtime.exception.PythonThreadKillException; 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.TruffleLanguage; import com.oracle.truffle.api.TruffleThreadBuilder; @@ -97,6 +110,15 @@ @CoreFunctions(defineModule = J__THREAD) public final class ThreadModuleBuiltins extends PythonBuiltins { + public static final StructSequence.BuiltinTypeDescriptor EXCEPTHOOK_ARGS_DESC = new StructSequence.BuiltinTypeDescriptor( + PythonBuiltinClassType.PExceptHookArgs, + 4, + new String[]{ + "exc_type", "exc_value", "exc_traceback", "thread"}, + new String[]{ + "Exception type", "Exception value", "Exception traceback", + "Exception thread"}); + @Override protected List> getNodeFactories() { return ThreadModuleBuiltinsFactory.getFactories(); @@ -106,6 +128,7 @@ protected List> getNodeFa public void initialize(Python3Core core) { addBuiltinConstant("error", core.lookupType(PythonBuiltinClassType.RuntimeError)); addBuiltinConstant("TIMEOUT_MAX", TIMEOUT_MAX); + StructSequence.initType(core, EXCEPTHOOK_ARGS_DESC); core.lookupBuiltinModule(T__THREAD).setModuleState(0); super.initialize(core); } @@ -173,6 +196,92 @@ static long getStackSize(VirtualFrame frame, Object stackSizeObj, } } + @Builtin(name = "_excepthook", minNumOfPositionalArgs = 2, declaresExplicitSelf = true) + @GenerateNodeFactory + abstract static class GetThreadExceptHookNode extends PythonBinaryBuiltinNode { + @Specialization + Object getExceptHook(@SuppressWarnings("unused") PythonModule self, + Object exceptHookArgs, + @Bind Node inliningTarget, + @Cached PRaiseNode raiseNode, + @Cached CallNode callNode, + @Cached PyObjectLookupAttr lookupAttr, + @Cached PyObjectSetAttr setAttr, + @Cached PyObjectStrAsTruffleStringNode strNode) { + + Object argsType = GetClassNode.GetPythonObjectClassNode.executeUncached((PythonObject) exceptHookArgs); + if (!TypeNodes.IsSameTypeNode.executeUncached(argsType, PythonBuiltinClassType.PExceptHookArgs)) { + throw PRaiseNode.getUncached().raise(raiseNode, PythonBuiltinClassType.TypeError, ErrorMessages.ARG_TYPE_MUST_BE, "_thread.excepthook", "ExceptHookArgs"); + } + SequenceStorage seq = ((PTuple) exceptHookArgs).getSequenceStorage(); + if (seq.length() != 4) { + throw PRaiseNode.getUncached().raise(raiseNode, PythonBuiltinClassType.TypeError, ErrorMessages.TAKES_EXACTLY_D_ARGUMENTS_D_GIVEN, 4, seq.length()); + } + + Object excType = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 0); + + if (TypeNodes.IsSameTypeNode.executeUncached(excType, PythonBuiltinClassType.SystemExit)) { + return PNone.NONE; + } + Object excValue = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 1); + Object excTraceback = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 2); + Object thread = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, 3); + + TruffleString name; + + Object nameAttr = lookupAttr.execute(null, inliningTarget, thread, tsLiteral("_name")); + if (nameAttr != null && nameAttr != PNone.NONE && nameAttr != PNone.NO_VALUE) { + name = strNode.execute(null, inliningTarget, nameAttr); + } else { + Object getIdentBuiltin = lookupAttr.execute(null, inliningTarget, thread, tsLiteral("get_ident")); + Object ident = callNode.executeWithoutFrame(getIdentBuiltin); + name = ident != null ? strNode.execute(null, inliningTarget, ident) : tsLiteral(""); + } + + Object sysMod = getContext().getSysModule(); + Object stdErr = lookupAttr.execute(null, inliningTarget, sysMod, T_STDERR); + + boolean stdErrInvalid = stdErr == null || stdErr == PNone.NONE || stdErr == PNone.NO_VALUE; + + if (stdErrInvalid) { + if (thread != null && thread != PNone.NONE && thread != PNone.NO_VALUE) { + stdErr = lookupAttr.execute(null, inliningTarget, thread, tsLiteral("_stderr")); + } + if (stdErr == null || stdErr == PNone.NONE || stdErr == PNone.NO_VALUE) { + return PNone.NONE; + } + } + + Object write = lookupAttr.execute(null, inliningTarget, stdErr, tsLiteral("write")); + Object flush = lookupAttr.execute(null, inliningTarget, stdErr, tsLiteral("flush")); + + callNode.executeWithoutFrame(write, tsLiteral("Exception in thread ")); + callNode.executeWithoutFrame(write, name); + callNode.executeWithoutFrame(write, tsLiteral(":\n")); + callNode.executeWithoutFrame(flush); + + Object sysExcepthook = lookupAttr.execute(null, inliningTarget, sysMod, T___EXCEPTHOOK__); + if (sysExcepthook != PNone.NO_VALUE && sysExcepthook != PNone.NONE) { + if (!stdErrInvalid) { + callNode.executeWithoutFrame(sysExcepthook, excType, excValue, excTraceback); + } else { + Object oldStdErr = lookupAttr.execute(null, inliningTarget, sysMod, T_STDERR); + try { + setAttr.execute(inliningTarget, sysMod, T_STDERR, stdErr); + callNode.executeWithoutFrame(sysExcepthook, excType, excValue, excTraceback); + } finally { + setAttr.execute(inliningTarget, sysMod, T_STDERR, oldStdErr == PNone.NO_VALUE ? PNone.NONE : oldStdErr); + } + } + callNode.executeWithoutFrame(flush); + } else if (excValue instanceof PBaseException) { + callNode.executeWithoutFrame(write, strNode.execute(null, inliningTarget, excValue)); + callNode.executeWithoutFrame(flush); + } + return PNone.NONE; + } + } + @Builtin(name = "start_new_thread", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 3) @Builtin(name = "start_new", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 3) @GenerateNodeFactory diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java index 33aa696318..a09bafc73a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/TimeModuleBuiltins.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. @@ -739,15 +739,17 @@ private static String truncYear(int year) { return yearstr.substring(yearstr.length() - 2); } - private static GregorianCalendar getCalendar(int[] time) { + private static GregorianCalendar getCalendar(int[] time, TimeZone timeZone) { Month month = Month.of(time[1]); // GregorianCalendar expect months that starts from 0 - return new GregorianCalendar(time[0], month.ordinal(), time[2], time[3], time[4], time[5]); + GregorianCalendar calendar = new GregorianCalendar(timeZone); + calendar.set(time[0], month.ordinal(), time[2], time[3], time[4], time[5]); + return calendar; } // This taken from JPython + some switches were corrected to provide the // same result as CPython @TruffleBoundary - public static TruffleString format(String format, int[] date, TruffleString.FromJavaStringNode fromJavaStringNode) { + public static TruffleString format(String format, int[] date, TimeZone timeZone, TruffleString.FromJavaStringNode fromJavaStringNode) { String s = ""; int lastc = 0; int j; @@ -869,7 +871,7 @@ public static TruffleString format(String format, int[] date, TruffleString.From // TODO this is not correct, CPython counts the week of year // from day of year item [8] if (cal == null) { - cal = getCalendar(date); + cal = getCalendar(date, timeZone); } cal.setFirstDayOfWeek(Calendar.SUNDAY); @@ -900,7 +902,7 @@ public static TruffleString format(String format, int[] date, TruffleString.From // from day of year item [8] if (cal == null) { - cal = getCalendar(date); + cal = getCalendar(date, timeZone); } cal.setFirstDayOfWeek(Calendar.MONDAY); cal.setMinimalDaysInFirstWeek(7); @@ -945,7 +947,7 @@ public static TruffleString format(String format, int[] date, TruffleString.From case 'Z': // timezone name if (cal == null) { - cal = getCalendar(date); + cal = getCalendar(date, timeZone); } // If items[8] == 1, we're in daylight savings time. // -1 means the information was not available; treat this as if not in dst. @@ -964,6 +966,16 @@ public static TruffleString format(String format, int[] date, TruffleString.From return fromJavaStringNode.execute(s, TS_ENCODING); } + @TruffleBoundary + public static TruffleString format(String format, int[] date, TruffleString.FromJavaStringNode fromJavaStringNode) { + return format(format, date, TimeZone.getDefault(), fromJavaStringNode); + } + + @TruffleBoundary + private static TimeZone getTimeZone(ZoneId currentZoneId) { + return TimeZone.getTimeZone(currentZoneId); + } + @Specialization static TruffleString formatTime(PythonModule module, TruffleString format, @SuppressWarnings("unused") PNone time, @Bind Node inliningTarget, @@ -972,11 +984,11 @@ static TruffleString formatTime(PythonModule module, TruffleString format, @Supp @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @Exclusive @Cached PRaiseNode raiseNode) { ModuleState moduleState = module.getModuleState(ModuleState.class); - return format(toJavaStringNode.execute(format), getIntLocalTimeStruct(moduleState.currentZoneId, (long) timeSeconds()), fromJavaStringNode); + return format(toJavaStringNode.execute(format), getIntLocalTimeStruct(moduleState.currentZoneId, (long) timeSeconds()), getTimeZone(moduleState.currentZoneId), fromJavaStringNode); } @Specialization - static TruffleString formatTime(VirtualFrame frame, @SuppressWarnings("unused") PythonModule module, TruffleString format, PTuple time, + static TruffleString formatTime(VirtualFrame frame, PythonModule module, TruffleString format, PTuple time, @Bind Node inliningTarget, @Cached SequenceStorageNodes.GetInternalObjectArrayNode getArray, @Cached PyNumberAsSizeNode asSizeNode, @@ -985,7 +997,7 @@ static TruffleString formatTime(VirtualFrame frame, @SuppressWarnings("unused") @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @Exclusive @Cached PRaiseNode raiseNode) { int[] date = checkStructtime(frame, inliningTarget, time, getArray, asSizeNode, raiseNode); - return format(toJavaStringNode.execute(format), date, fromJavaStringNode); + return format(toJavaStringNode.execute(format), date, getTimeZone(module.getModuleState(ModuleState.class).currentZoneId), fromJavaStringNode); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/UnicodeDataModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/UnicodeDataModuleBuiltins.java index a5f1ef49d6..d06c920a55 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/UnicodeDataModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/UnicodeDataModuleBuiltins.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 @@ -43,6 +43,8 @@ import static com.oracle.graal.python.nodes.BuiltinNames.J_UNICODEDATA; import static com.oracle.graal.python.nodes.BuiltinNames.T_UNICODEDATA; import static com.oracle.graal.python.nodes.BuiltinNames.T___GRAALPYTHON__; +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__; +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___QUALNAME__; import static com.oracle.graal.python.runtime.exception.PythonErrorType.KeyError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; @@ -56,21 +58,34 @@ import org.graalvm.shadowed.com.ibm.icu.text.Normalizer2; import org.graalvm.shadowed.com.ibm.icu.util.VersionInfo; +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.builtins.CoreFunctions; 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.module.PythonModule; +import com.oracle.graal.python.builtins.objects.object.PythonObject; +import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass; +import com.oracle.graal.python.builtins.objects.type.PythonClass; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; 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.object.GetOrCreateDictNode; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; +import com.oracle.graal.python.runtime.PythonOptions; +import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.graal.python.nodes.statement.AbstractImportNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; @@ -80,6 +95,7 @@ import com.oracle.truffle.api.dsl.ImportStatic; 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; import com.oracle.truffle.api.strings.TruffleString.FromJavaStringNode; @@ -87,6 +103,11 @@ @CoreFunctions(defineModule = J_UNICODEDATA) public final class UnicodeDataModuleBuiltins extends PythonBuiltins { + private static final TruffleString T__CPYTHON_UNICODEDATA = toTruffleStringUncached("_cpython_unicodedata"); + private static final TruffleString T_LOOKUP = toTruffleStringUncached("lookup"); + private static final TruffleString T_UCD_3_2_0 = toTruffleStringUncached("ucd_3_2_0"); + private static final TruffleString T_UNIDATA_VERSION = toTruffleStringUncached("unidata_version"); + @Override protected List> getNodeFactories() { return UnicodeDataModuleBuiltinsFactory.getFactories(); @@ -120,12 +141,31 @@ private static String getUnicodeNameTB(int cp) { public void postInitialize(Python3Core core) { super.postInitialize(core); PythonModule self = core.lookupBuiltinModule(T_UNICODEDATA); - self.setAttribute(toTruffleStringUncached("unidata_version"), toTruffleStringUncached(getUnicodeVersion())); - PyObjectCallMethodObjArgs.executeUncached(core.lookupBuiltinModule(T___GRAALPYTHON__), toTruffleStringUncached("import_current_as_named_module_with_delegate"), - /* module_name= */ T_UNICODEDATA, - /* delegate_name= */ toTruffleStringUncached("_cpython_unicodedata"), - /* delegate_attributes= */ PFactory.createList(core.getLanguage(), new Object[]{toTruffleStringUncached("ucd_3_2_0")}), - /* owner_globals= */ GetOrCreateDictNode.executeUncached(self)); + self.setAttribute(T_UNIDATA_VERSION, toTruffleStringUncached(getUnicodeVersion())); + if (core.getLanguage().getEngineOption(PythonOptions.UnicodeCharacterDatabaseNativeFallback)) { + PyObjectCallMethodObjArgs.executeUncached(core.lookupBuiltinModule(T___GRAALPYTHON__), toTruffleStringUncached("import_current_as_named_module_with_delegate"), + /* module_name= */ T_UNICODEDATA, + /* delegate_name= */ T__CPYTHON_UNICODEDATA, + /* delegate_attributes= */ PFactory.createList(core.getLanguage(), new Object[]{T_UCD_3_2_0}), + /* owner_globals= */ GetOrCreateDictNode.executeUncached(self)); + } else { + self.setAttribute(T_UCD_3_2_0, createUCDCompatibilityObject(core, self)); + } + } + + private static PythonObject createUCDCompatibilityObject(Python3Core core, PythonModule self) { + TruffleString t_ucd = toTruffleStringUncached("UCD"); + PythonClass clazz = PFactory.createPythonClassAndFixupSlots(null, core.getLanguage(), t_ucd, PythonBuiltinClassType.PythonObject, + new PythonAbstractClass[]{core.lookupType(PythonBuiltinClassType.PythonObject)}); + for (String s : new String[]{"normalize", "is_normalized", "lookup", "name", "bidirectional", "category", "combining", "east_asian_width", "decomposition", "digit", "decimal"}) { + TruffleString ts = toTruffleStringUncached(s); + clazz.setAttribute(ts, PFactory.createStaticmethodFromCallableObj(core.getLanguage(), self.getAttribute(ts))); + } + clazz.setAttribute(T___MODULE__, T_UNICODEDATA); + clazz.setAttribute(T___QUALNAME__, t_ucd); + PythonObject obj = PFactory.createPythonObject(clazz, clazz.getInstanceShape()); + obj.setAttribute(T_UNIDATA_VERSION, toTruffleStringUncached("3.2.0")); + return obj; } static final int NORMALIZER_FORM_COUNT = 4; @@ -212,25 +252,25 @@ abstract static class LookupNode extends PythonUnaryClinicBuiltinNode { private static final int NAME_MAX_LENGTH = 256; @Specialization - @TruffleBoundary - static Object lookup(TruffleString name, - @Bind Node inliningTarget) { + static Object lookup(VirtualFrame frame, TruffleString name, + @Bind PythonLanguage lang, + @Bind Node inliningTarget, + @Cached("createFor($node)") BoundaryCallData boundaryCallData) { String nameString = ToJavaStringNode.getUncached().execute(name); if (nameString.length() > NAME_MAX_LENGTH) { throw PRaiseNode.raiseStatic(inliningTarget, KeyError, ErrorMessages.NAME_TOO_LONG); } - // TODO: support Unicode character named sequences (GR-68227) - // see test/test_ucn.py.UnicodeFunctionsTest.test_named_sequences_full - String character = getCharacterByUnicodeName(nameString); - if (character == null) { - character = getCharacterByUnicodeNameAlias(nameString); - } - if (character == null) { - throw PRaiseNode.raiseStatic(inliningTarget, KeyError, ErrorMessages.UNDEFINED_CHARACTER_NAME, name); + Object saved = BoundaryCallContext.enter(frame, boundaryCallData); + try { + Object result = lookupBoundary(lang, nameString, name); + if (result != null) { + return result; + } + } finally { + BoundaryCallContext.exit(frame, boundaryCallData, saved); } - - return FromJavaStringNode.getUncached().execute(character, TS_ENCODING); + throw PRaiseNode.raiseStatic(inliningTarget, KeyError, ErrorMessages.UNDEFINED_CHARACTER_NAME, name); } @Override @@ -238,10 +278,6 @@ protected ArgumentClinicProvider getArgumentClinic() { return UnicodeDataModuleBuiltinsClinicProviders.LookupNodeClinicProviderGen.INSTANCE; } - /** - * Finds a Unicode code point by its Unicode name and returns it as a single character - * String. Returns null if name is not found. - */ @TruffleBoundary private static String getCharacterByUnicodeName(String unicodeName) { int codepoint = UCharacter.getCharFromName(unicodeName); @@ -253,10 +289,6 @@ private static String getCharacterByUnicodeName(String unicodeName) { return UCharacter.toString(codepoint); } - /** - * Finds a Unicode code point by its Unicode name alias and returns it as a single character - * String. Returns null if name alias is not found. - */ @TruffleBoundary private static String getCharacterByUnicodeNameAlias(String unicodeName) { int codepoint = UCharacter.getCharFromNameAlias(unicodeName); @@ -267,6 +299,33 @@ private static String getCharacterByUnicodeNameAlias(String unicodeName) { return UCharacter.toString(codepoint); } + + @TruffleBoundary + private static Object lookupBoundary(PythonLanguage lang, String nameString, TruffleString name) { + String character = getCharacterByUnicodeName(nameString); + if (character == null) { + character = getCharacterByUnicodeNameAlias(nameString); + } + if (character != null) { + return FromJavaStringNode.getUncached().execute(character, TS_ENCODING); + } + return lookupNamedSequenceFromFallback(lang, name); + } + + private static Object lookupNamedSequenceFromFallback(PythonLanguage lang, TruffleString name) { + if (lang.getEngineOption(PythonOptions.UnicodeCharacterDatabaseNativeFallback)) { + try { + PythonModule cpythonUnicodeData = AbstractImportNode.importModule(T__CPYTHON_UNICODEDATA); + Object lookup = PyObjectGetAttr.executeUncached(cpythonUnicodeData, T_LOOKUP); + return CallNode.executeUncached(lookup, name); + } catch (PException e) { + if (!IsBuiltinObjectProfile.profileObjectUncached(e.getUnreifiedException(), PythonBuiltinClassType.ImportError)) { + throw e; + } + } + } + return null; + } } // unicodedata.name(chr, default) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java index e81bb0bbe2..a303144e2f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java @@ -76,11 +76,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.BuiltinFunctions.IsSubClassNode; import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltinsClinicProviders.WarnBuiltinNodeClinicProviderGen; import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltinsFactory.WarnBuiltinNodeFactory; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.code.PCode; 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.frame.PFrame; @@ -96,6 +94,7 @@ import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectIsTrueNode; +import com.oracle.graal.python.lib.PyObjectIsSubclassNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectRichCompareBool.CachedPyObjectRichCompareBool; @@ -140,7 +139,6 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.strings.TruffleString; @@ -212,7 +210,7 @@ static final class WarningsModuleNode extends Node { @Child GetClassNode getClassNode; @Child PyNumberAsSizeNode asSizeNode; @Child PyObjectIsTrueNode isTrueNode; - @Child IsSubClassNode isSubClassNode; + @Child PyObjectIsSubclassNode isSubClassNode; @Child GetOrCreateDictNode getDictNode; @Child ReadFrameNode readFrameNode; @Child PyObjectLookupAttr lookupAttrNode; @@ -378,11 +376,11 @@ private TruffleString.SubstringNode getSubstringNode() { return substringNode; } - private IsSubClassNode getIsSubClass() { + private PyObjectIsSubclassNode getIsSubClass() { if (isSubClassNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); reportPolymorphicSpecialize(); - isSubClassNode = insert(IsSubClassNode.create()); + isSubClassNode = insert(PyObjectIsSubclassNode.create()); } return isSubClassNode; } @@ -403,22 +401,21 @@ private PFrame getCallerFrame(VirtualFrame frame, int stackLevel, TruffleString[ readFrameNode = insert(ReadFrameNode.create()); } ReadFrameNode.FrameSelector selector = ReadFrameNode.VisiblePythonFramesSelector.INSTANCE; - if (skipFilePrefixes != null) { - /* - * CPython would always count the first frame into the stacklevel even if it is - * supposed to be skipped. We do that too, but our code is one off because of the - * builtin frame. Let's just assume the common case where the first python frame is - * always skipped by the filter. - */ - stackLevel--; - selector = rootNode -> ReadFrameNode.VisiblePythonFramesSelector.INSTANCE.skip(rootNode) || isFilenameToSkip(skipFilePrefixes, rootNode); - } - return readFrameNode.getFrameForReference(frame, PArguments.getCurrentFrameInfo(frame), selector, stackLevel, CallerFlags.NEEDS_LASTI); + if (skipFilePrefixes != null && stackLevel > 0) { + PFrame currentFrame = readFrameNode.getCurrentPythonFrame(frame, CallerFlags.NEEDS_LASTI); + while (--stackLevel > 0 && currentFrame != null) { + do { + currentFrame = readFrameNode.getFrameForReference(frame, currentFrame.getRef(), selector, 1, CallerFlags.NEEDS_LASTI); + } while (currentFrame != null && isFilenameToSkip(currentFrame.getCode().getFilename(), skipFilePrefixes)); + } + return currentFrame; + } else { + return readFrameNode.getFrameForReference(frame, PArguments.getCurrentFrameInfo(frame), selector, stackLevel - 1, CallerFlags.NEEDS_LASTI); + } } @TruffleBoundary - private static boolean isFilenameToSkip(TruffleString[] skipFilePrefixes, RootNode rootNode) { - TruffleString fileName = PCode.extractFileName(rootNode); + private static boolean isFilenameToSkip(TruffleString fileName, TruffleString[] skipFilePrefixes) { for (TruffleString prefix : skipFilePrefixes) { if (fileName.byteLength(TS_ENCODING) >= prefix.byteLength(TS_ENCODING) && prefix.regionEqualByteIndexUncached(0, fileName, 0, prefix.byteLength(TS_ENCODING), TS_ENCODING)) { @@ -598,7 +595,7 @@ private TruffleString getFilter(VirtualFrame frame, BoundaryCallData boundaryCal boolean goodMsg = checkMatched(frame, msg, text); boolean goodMod = checkMatched(frame, mod, module); - boolean isSubclass = getIsSubClass().executeBoolean(frame, category, cat); + boolean isSubclass = getIsSubClass().execute(frame, category, cat); int ln = getAsSizeNode().executeExactCached(frame, lnObj); if (goodMsg && isSubclass && goodMod && (ln == 0 || lineno == ln)) { // if we're ignoring warnings, the first action will match all and the loop @@ -781,7 +778,7 @@ private void warnExplicit(VirtualFrame frame, PythonModule warnings, // Python code uses PyObject_IsInstance but on the built-in Warning class, so we know // what __instancecheck__ does Object text; - if (getIsSubClass().executeBoolean(frame, getPythonClass(message), PythonBuiltinClassType.Warning)) { + if (getIsSubClass().execute(frame, getPythonClass(message), PythonBuiltinClassType.Warning)) { text = getStrNode().executeCached(frame, message); category = getPythonClass(message); } else { @@ -871,9 +868,7 @@ private void warnExplicitPart2(PythonContext context, PythonModule warnings, Tru * Used from doWarn. On the fast path. */ private void setupContext(VirtualFrame frame, int stackLevel, TruffleString[] skipFilePrefixes, TruffleString[] filename, int[] lineno, TruffleString[] module, Object[] registry) { - // the stack level for the intrinsified version is off-by-one compared to the Python - // version - PFrame f = frame == null ? null : getCallerFrame(frame, stackLevel - 1, skipFilePrefixes); + PFrame f = frame == null ? null : getCallerFrame(frame, stackLevel, skipFilePrefixes); PDict globals; if (f == null || f.getGlobals() == null) { globals = getSysDict(); @@ -884,7 +879,7 @@ private void setupContext(VirtualFrame frame, int stackLevel, TruffleString[] sk lineno[0] = f.getLine(); RootCallTarget ct = f.getTarget(); if (ct != null) { - filename[0] = PCode.extractFileName(ct.getRootNode()); + filename[0] = f.getCode().getFilename(); } else { filename[0] = T_UNKNOWN_SOURCE; } @@ -912,11 +907,11 @@ private void setupContext(VirtualFrame frame, int stackLevel, TruffleString[] sk */ private Object getCategory(VirtualFrame frame, Object message, Object category) { Object messageType = getPythonClass(message); - if (getIsSubClass().executeBoolean(frame, messageType, PythonBuiltinClassType.Warning)) { + if (getIsSubClass().execute(frame, messageType, PythonBuiltinClassType.Warning)) { return messageType; } else if (category == null || category == PNone.NONE) { return PythonBuiltinClassType.UserWarning; - } else if (!getIsTypeNode().executeCached(category) || !getIsSubClass().executeBoolean(frame, category, PythonBuiltinClassType.Warning)) { + } else if (!getIsTypeNode().executeCached(category) || !getIsSubClass().execute(frame, category, PythonBuiltinClassType.Warning)) { throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.TypeError, ErrorMessages.CATEGORY_MUST_BE_WARN_SUBCLS, category); } else { return category; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WinregLegacyModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WinregLegacyModuleBuiltins.java new file mode 100644 index 0000000000..bd48f6dc6f --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WinregLegacyModuleBuiltins.java @@ -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. + */ +package com.oracle.graal.python.builtins.modules; + +import java.util.List; + +import com.oracle.graal.python.annotations.PythonOS; +import com.oracle.graal.python.builtins.CoreFunctions; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; +import com.oracle.truffle.api.dsl.NodeFactory; + +@CoreFunctions(defineModule = "_winreg", os = PythonOS.PLATFORM_WIN32) +public final class WinregLegacyModuleBuiltins extends PythonBuiltins { + @Override + protected List> getNodeFactories() { + return List.of(); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ast/Obj2SstBase.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ast/Obj2SstBase.java index 056a01293b..ab1295864d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ast/Obj2SstBase.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ast/Obj2SstBase.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 @@ -41,6 +41,7 @@ package com.oracle.graal.python.builtins.modules.ast; import static com.oracle.graal.python.builtins.modules.ast.AstState.T_C_CONSTANT; +import static com.oracle.graal.python.builtins.modules.ast.AstState.T_C_MATCHSINGLETON; import static com.oracle.graal.python.builtins.modules.ast.AstState.T_F_VALUE; import static com.oracle.graal.python.nodes.ErrorMessages.AST_IDENTIFIER_MUST_BE_OF_TYPE_STR; import static com.oracle.graal.python.nodes.ErrorMessages.EXPECTED_SOME_SORT_OF_S_BUT_GOT_S; @@ -71,6 +72,7 @@ import com.oracle.graal.python.lib.IteratorExhausted; import com.oracle.graal.python.lib.PyBytesCheckExactNode; import com.oracle.graal.python.lib.PyComplexCheckExactNode; +import com.oracle.graal.python.lib.PyEnterRecursiveCallNode; import com.oracle.graal.python.lib.PyFloatCheckExactNode; import com.oracle.graal.python.lib.PyFrozenSetCheckExactNode; import com.oracle.graal.python.lib.PyIterNextNode; @@ -89,6 +91,7 @@ import com.oracle.graal.python.nodes.util.CastToJavaBooleanNode; import com.oracle.graal.python.nodes.util.CastToJavaStringNode; import com.oracle.graal.python.pegparser.sst.ConstantValue; +import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.nodes.Node; @@ -124,12 +127,16 @@ T lookupAndConvert(Object obj, TruffleString attrName, TruffleString nodeNam // since our SST nodes are in the pegparser project which does not have access to Python // exceptions. So we handle PNone.NONE here, but there is one exception - None is a // valid value for the required field ExprTy.Constant.value. - if (!(nodeName == T_C_CONSTANT && attrName == T_F_VALUE)) { + if (!((nodeName == T_C_CONSTANT || nodeName == T_C_MATCHSINGLETON) && attrName == T_F_VALUE)) { throw raiseValueError(FIELD_S_IS_REQUIRED_FOR_S, attrName, nodeName); } } - // Py_EnterRecursiveCall(" while traversing '%s' node") - return conversion.convert(tmp); + PythonThreadState threadState = PyEnterRecursiveCallNode.enterUncached(node, ErrorMessages.MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_TRAVERSING_S_NODE, nodeName); + try { + return conversion.convert(tmp); + } finally { + PyEnterRecursiveCallNode.leave(threadState); + } } int lookupAndConvertInt(Object obj, TruffleString attrName, TruffleString nodeName) { @@ -140,8 +147,12 @@ int lookupAndConvertInt(Object obj, TruffleString attrName, TruffleString nodeNa } // PNone.NONE is handled by obj2int() (produces a different error message) } - // Py_EnterRecursiveCall(" while traversing '%s' node") - return obj2int(tmp); + PythonThreadState threadState = PyEnterRecursiveCallNode.enterUncached(node, ErrorMessages.MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_TRAVERSING_S_NODE, nodeName); + try { + return obj2int(tmp); + } finally { + PyEnterRecursiveCallNode.leave(threadState); + } } int lookupAndConvertIntOpt(Object obj, TruffleString attrName, @SuppressWarnings("unused") TruffleString nodeName, int defaultValue) { @@ -160,8 +171,12 @@ boolean lookupAndConvertBoolean(Object obj, TruffleString attrName, TruffleStrin } // PNone.NONE is handled by obj2boolean() (produces a different error message) } - // Py_EnterRecursiveCall(" while traversing '%s' node") - return obj2boolean(tmp); + PythonThreadState threadState = PyEnterRecursiveCallNode.enterUncached(node, ErrorMessages.MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_TRAVERSING_S_NODE, nodeName); + try { + return obj2boolean(tmp); + } finally { + PyEnterRecursiveCallNode.leave(threadState); + } } T[] lookupAndConvertSequence(Object obj, TruffleString attrName, TruffleString nodeName, Conversion conversion, IntFunction arrayFactory) { @@ -176,8 +191,12 @@ T[] lookupAndConvertSequence(Object obj, TruffleString attrName, TruffleStri T[] result = arrayFactory.apply(seq.length()); for (int i = 0; i < result.length; ++i) { tmp = SequenceStorageNodes.GetItemScalarNode.executeUncached(seq, i); - // Py_EnterRecursiveCall(" while traversing '%s' node") - result[i] = conversion.convert(tmp); + PythonThreadState threadState = PyEnterRecursiveCallNode.enterUncached(node, ErrorMessages.MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_TRAVERSING_S_NODE, nodeName); + try { + result[i] = conversion.convert(tmp); + } finally { + PyEnterRecursiveCallNode.leave(threadState); + } if (result.length != seq.length()) { throw raiseTypeError(S_FIELD_S_CHANGED_SIZE_DURING_ITERATION, nodeName, attrName); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ast/Validator.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ast/Validator.java index 1607f75056..e2ea6b7801 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ast/Validator.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ast/Validator.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 @@ -1006,9 +1006,9 @@ public Void visit(TypeVarTuple node) { return null; } -/*- -// Validation of sequences -*/ + /*- + // Validation of sequences + */ // Equivalent of validate_stmts private void validateStmts(StmtTy[] stmts) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2CompressorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2CompressorBuiltins.java index e1963476f4..7f5a86cf00 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2CompressorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2CompressorBuiltins.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,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.runtime.GilNode; -import com.oracle.graal.python.runtime.NFIBz2Support; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeBz2Support; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; @@ -123,15 +122,13 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization(guards = {"compresslevel >= 1", "compresslevel <= 9"}) PNone init(BZ2Object.BZ2Compressor self, int compresslevel, @Bind Node inliningTarget, - @Cached NativeLibrary.InvokeNativeFunction createStream, - @Cached NativeLibrary.InvokeNativeFunction compressInit, @Cached GilNode gil, @Cached PRaiseNode raiseNode) { gil.release(true); try { - NFIBz2Support bz2Support = PythonContext.get(this).getNFIBz2Support(); - Object bzst = bz2Support.createStream(createStream); - int err = bz2Support.compressInit(bzst, compresslevel, compressInit); + NativeBz2Support bz2Support = PythonContext.get(this).getNativeBz2Support(); + long bzst = bz2Support.createStream(); + int err = bz2Support.compressInit(bzst, compresslevel); if (err != BZ_OK) { errorHandling(inliningTarget, err, raiseNode); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2DecompressorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2DecompressorBuiltins.java index 56a7b84b9c..999ab0d369 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2DecompressorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2DecompressorBuiltins.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 @@ -68,8 +68,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonTernaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; -import com.oracle.graal.python.runtime.NFIBz2Support; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeBz2Support; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; @@ -111,12 +110,10 @@ public abstract static class InitNode extends PythonUnaryBuiltinNode { @Specialization static PNone init(BZ2Object.BZ2Decompressor self, @Bind Node inliningTarget, - @Cached NativeLibrary.InvokeNativeFunction createStream, - @Cached NativeLibrary.InvokeNativeFunction compressInit, @Cached PRaiseNode raiseNode) { - NFIBz2Support bz2Support = PythonContext.get(inliningTarget).getNFIBz2Support(); - Object bzst = bz2Support.createStream(createStream); - int err = bz2Support.decompressInit(bzst, compressInit); + NativeBz2Support bz2Support = PythonContext.get(inliningTarget).getNativeBz2Support(); + long bzst = bz2Support.createStream(); + int err = bz2Support.decompressInit(bzst); if (err != BZ_OK) { errorHandling(inliningTarget, err, raiseNode); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2Object.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2Object.java index 72e88d8640..6e90f4c6df 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2Object.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/BZ2Object.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 @@ -44,40 +44,45 @@ import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; -import com.oracle.graal.python.runtime.NFIBz2Support; +import com.oracle.graal.python.runtime.NativeBz2Support; 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.object.Shape; public abstract class BZ2Object extends PythonBuiltinObject { - private NFIBz2Support.Pointer pointer; + private NativeBz2Support.Pointer pointer; - public BZ2Object(Object cls, Shape instanceShape) { + BZ2Object(Object cls, Shape instanceShape) { super(cls, instanceShape); } - public final void init(Object bzst, NFIBz2Support lib) { - this.pointer = new NFIBz2Support.Pointer(this, bzst, lib); + public final void init(long bzst, NativeBz2Support lib) { + this.pointer = new NativeBz2Support.Pointer(this, bzst, lib); + assert !pointer.isReleased(); } - public final Object getBzs() { - assert pointer != null; - return pointer.getReference(); + public final long getBzs() { + NativeBz2Support.Pointer p = pointer; + assert p != null && !p.isReleased(); + return p.getPointer(); } - @CompilerDirectives.TruffleBoundary + @TruffleBoundary public final void markReleased() { - if (pointer != null) { - synchronized (this) { - pointer.markReleased(); - pointer = null; - } + NativeBz2Support.Pointer p; + synchronized (this) { + p = pointer; + pointer = null; + } + if (p != null) { + boolean markedReleased = p.markReleased(); + assert markedReleased || p.isReleased(); } } - public static class BZ2Compressor extends BZ2Object { + public static final class BZ2Compressor extends BZ2Object { private boolean flushed; @@ -95,7 +100,7 @@ public void setFlushed() { } } - public static class BZ2Decompressor extends BZ2Object { + public static final class BZ2Decompressor extends BZ2Object { private boolean eof; private byte[] unusedData; @@ -221,12 +226,4 @@ public void setBzsAvailInReal(long bzsAvailInReal) throws OverflowException { this.bzsAvailInReal = PInt.intValueExact(bzsAvailInReal); } } - - public static BZ2Compressor createCompressor(Object cls, Shape instanceShape) { - return new BZ2Compressor(cls, instanceShape); - } - - public static BZ2Decompressor createDecompressor(Object cls, Shape instanceShape) { - return new BZ2Decompressor(cls, instanceShape); - } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/Bz2Nodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/Bz2Nodes.java index 984ba4be56..392e3b6ca2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/Bz2Nodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/bz2/Bz2Nodes.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 @@ -57,8 +57,7 @@ import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.runtime.NFIBz2Support; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeBz2Support; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.util.OverflowException; import com.oracle.graal.python.util.PythonUtils; @@ -111,11 +110,10 @@ public byte[] flush(BZ2Object.BZ2Compressor self, PythonContext context) { @Specialization static byte[] nativeCompress(BZ2Object.BZ2Compressor self, PythonContext context, byte[] bytes, int len, int action, @Bind Node inliningTarget, - @Cached NativeLibrary.InvokeNativeFunction compress, @Cached GetOutputNativeBufferNode getBuffer, @Cached PRaiseNode raiseNode) { - NFIBz2Support bz2Support = context.getNFIBz2Support(); - int err = bz2Support.compress(self.getBzs(), bytes, len, action, INITIAL_BUFFER_SIZE, compress); + NativeBz2Support bz2Support = context.getNativeBz2Support(); + int err = bz2Support.compress(self.getBzs(), bytes, len, action, INITIAL_BUFFER_SIZE); if (err != BZ_OK) { errorHandling(inliningTarget, err, raiseNode); } @@ -230,20 +228,17 @@ public abstract static class Bz2NativeInternalDecompress extends Node { @Specialization static byte[] nativeInternalDecompress(BZ2Object.BZ2Decompressor self, int maxLength, @Bind Node inliningTarget, - @Cached NativeLibrary.InvokeNativeFunction decompress, - @Cached NativeLibrary.InvokeNativeFunction getBzsAvailInReal, - @Cached NativeLibrary.InvokeNativeFunction getNextInIndex, @Cached GetOutputNativeBufferNode getBuffer, @Cached InlinedConditionProfile errProfile, @Cached InlinedBranchProfile ofProfile, @Cached PRaiseNode raiseNode) { PythonContext context = PythonContext.get(inliningTarget); - NFIBz2Support bz2Support = context.getNFIBz2Support(); + NativeBz2Support bz2Support = context.getNativeBz2Support(); byte[] in = self.getNextIn(); int offset = self.getNextInIndex(); - int err = bz2Support.decompress(self.getBzs(), in, offset, maxLength, INITIAL_BUFFER_SIZE, self.getBzsAvailInReal(), decompress); - long nextInIdx = bz2Support.getNextInIndex(self.getBzs(), getNextInIndex); - long bzsAvailInReal = bz2Support.getBzsAvailInReal(self.getBzs(), getBzsAvailInReal); + int err = bz2Support.decompress(self.getBzs(), in, offset, maxLength, INITIAL_BUFFER_SIZE, self.getBzsAvailInReal()); + long nextInIdx = bz2Support.getNextInIndex(self.getBzs()); + long bzsAvailInReal = bz2Support.getBzsAvailInReal(self.getBzs()); try { self.setNextInIndex(nextInIdx); self.setBzsAvailInReal(bzsAvailInReal); @@ -264,18 +259,16 @@ static byte[] nativeInternalDecompress(BZ2Object.BZ2Decompressor self, int maxLe @GenerateCached(false) public abstract static class GetOutputNativeBufferNode extends Node { - public abstract byte[] execute(Node inliningTarget, Object bzst, PythonContext context); + public abstract byte[] execute(Node inliningTarget, long bzst, PythonContext context); @Specialization - static byte[] getBuffer(Node inliningTarget, Object bzst, PythonContext context, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getBufferSize, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getBuffer, + static byte[] getBuffer(Node inliningTarget, long bzst, PythonContext context, @Cached InlinedBranchProfile ofProfile, @Cached PRaiseNode raiseNode) { - NFIBz2Support bz2Support = context.getNFIBz2Support(); + NativeBz2Support bz2Support = context.getNativeBz2Support(); int size; try { - size = PInt.intValueExact(bz2Support.getOutputBufferSize(bzst, getBufferSize)); + size = PInt.intValueExact(bz2Support.getOutputBufferSize(bzst)); } catch (OverflowException of) { ofProfile.enter(inliningTarget); throw raiseNode.raise(inliningTarget, SystemError, VALUE_TOO_LARGE_TO_FIT_INTO_INDEX); @@ -285,7 +278,7 @@ static byte[] getBuffer(Node inliningTarget, Object bzst, PythonContext context, } byte[] resultArray = new byte[size]; /* this will clear the native output once retrieved */ - bz2Support.getOutputBuffer(bzst, resultArray, getBuffer); + bz2Support.getOutputBuffer(bzst, resultArray); return resultArray; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/CFunctionDocUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/CFunctionDocUtils.java new file mode 100644 index 0000000000..97533eafb9 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/CFunctionDocUtils.java @@ -0,0 +1,112 @@ +/* + * 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.modules.cext; + +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___TEXT_SIGNATURE__; +import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; + +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; +import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.strings.TruffleString; + +public final class CFunctionDocUtils { + + private static final String SIGNATURE_END_MARKER = ")\n--\n\n"; + private static final int SIGNATURE_END_MARKER_LENGTH = SIGNATURE_END_MARKER.length(); + + private CFunctionDocUtils() { + } + + @TruffleBoundary + public static void writeDocAndTextSignature(PBuiltinFunction function, TruffleString name, Object docObj) { + Object doc = PNone.NONE; + Object textSignature = PNone.NONE; + if (docObj instanceof TruffleString) { + TruffleString docTruffleString = (TruffleString) docObj; + String docString = docTruffleString.toJavaStringUncached(); + int start = findSignature(name.toJavaStringUncached(), docString); + int end = start >= 0 ? skipSignature(docString, start) : -1; + if (end < 0) { + doc = docString.isEmpty() ? PNone.NONE : docTruffleString; + } else { + int textSignatureEnd = end - SIGNATURE_END_MARKER_LENGTH + 1; + doc = end == docString.length() ? PNone.NONE : toTruffleStringUncached(docString.substring(end)); + textSignature = toTruffleStringUncached(docString.substring(start, textSignatureEnd)); + } + } + WriteAttributeToPythonObjectNode.executeUncached(function, T___DOC__, doc); + WriteAttributeToPythonObjectNode.executeUncached(function, T___TEXT_SIGNATURE__, textSignature); + } + + /* + * Matches CPython's find_signature: the internal doc must start with the callable name followed + * by the first '(' of the signature. + */ + private static int findSignature(String name, String doc) { + int dot = name.lastIndexOf('.'); + if (dot != -1) { + name = name.substring(dot + 1); + } + int length = name.length(); + if (!doc.startsWith(name) || doc.length() <= length || doc.charAt(length) != '(') { + return -1; + } + return length; + } + + /* + * Matches CPython's skip_signature: a blank line before the marker invalidates the signature. + */ + private static int skipSignature(String doc, int start) { + for (int i = start; i < doc.length(); i++) { + if (doc.startsWith(SIGNATURE_END_MARKER, i)) { + return i + SIGNATURE_END_MARKER_LENGTH; + } + if (doc.charAt(i) == '\n' && i + 1 < doc.length() && doc.charAt(i + 1) == '\n') { + return -1; + } + } + return -1; + } +} 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..18dc6e8b17 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 @@ -40,18 +40,22 @@ */ package com.oracle.graal.python.builtins.modules.cext; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.DeprecationWarning; 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,43 +64,43 @@ 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.nodes.SpecialMethodNames.T___INDEX__; +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; import com.oracle.graal.python.builtins.modules.BuiltinFunctions.HexNode; import com.oracle.graal.python.builtins.modules.BuiltinFunctions.OctNode; +import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins; 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.NativeToPythonInternalNode; +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.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; import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotMpAssSubscript.CallSlotMpAssSubscriptNode; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotUnaryFunc.CallSlotUnaryNode; import com.oracle.graal.python.lib.IteratorExhausted; import com.oracle.graal.python.lib.PyIterCheckNode; import com.oracle.graal.python.lib.PyIterNextNode; +import com.oracle.graal.python.lib.PyLongCheckExactNode; +import com.oracle.graal.python.lib.PyLongCheckNode; +import com.oracle.graal.python.lib.PyLongCopyNodeGen; import com.oracle.graal.python.lib.PyNumberAddNode; import com.oracle.graal.python.lib.PyNumberAndNode; import com.oracle.graal.python.lib.PyNumberDivmodNode; @@ -131,6 +135,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 +148,72 @@ 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(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Index(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + checkNonNullArgUncached(obj); + if (PyLongCheckNode.executeUncached(obj)) { + return PythonToNativeInternalNode.executeNewRefUncached(obj); + } + TpSlots slots = GetObjectSlotsNode.executeUncached(obj); + if (slots.nb_index() == null) { + throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.OBJ_CANNOT_BE_INTERPRETED_AS_INTEGER, obj); + } + Object result = CallSlotUnaryNode.executeUncached(slots.nb_index(), obj); + if (PyLongCheckExactNode.executeUncached(result)) { + return PythonToNativeInternalNode.executeNewRefUncached(result); } + if (!PyLongCheckNode.executeUncached(result)) { + throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.INDEX_RETURNED_NON_INT, result); + } + WarningsModuleBuiltins.WarnNode.getUncached().warnFormat(null, null, DeprecationWarning, 1, + ErrorMessages.WARN_P_RETURNED_NON_P, obj, T___INDEX__, "int", result, "int"); + return PythonToNativeInternalNode.executeNewRefUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyNumber_Long extends CApiUnaryBuiltinNode { + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_IndexCopy(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + Object result = PyLongCopyNodeGen.getUncached().execute(null, obj); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } - @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 = NativeToPythonInternalNode.executeUncached(objectPtr, false); + Object result = PyNumberLongNode.executeUncached(object); + return PythonToNativeInternalNode.executeNewRefUncached(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 +262,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 = NativeToPythonInternalNode.executeUncached(objectPtr, false); + double result = PyNumberFloatNode.executeUncached(object); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberAddNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberSubtractNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberMultiplyNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberRemainderNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberTrueDivideNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberFloorDivideNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberDivmodNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberAndNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberOrNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberXorNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberLshiftNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberRshiftNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberMatrixMultiplyNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceAddNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceSubtractNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceMultiplyNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceRemainderNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceTrueDivideNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceFloorDivideNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceAndNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceOrNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceXorNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceLshiftNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceRshiftNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object result = PyNumberInPlaceMatrixMultiplyNode.getUncached().execute(null, o1, o2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object o3 = NativeToPythonInternalNode.executeUncached(o3Ptr, false); + Object result = PyNumberInPlacePowerNode.getUncached().execute(null, o1, o2, o3); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(o1Ptr, false); + Object o2 = NativeToPythonInternalNode.executeUncached(o2Ptr, false); + Object o3 = NativeToPythonInternalNode.executeUncached(o3Ptr, false); + Object result = PyNumberPowerNode.getUncached().execute(null, o1, o2, o3); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + checkNonNullArgUncached(obj); + Object result = GetClassNode.executeUncached(obj) == PythonBuiltinClassType.PTuple ? obj : ConstructTupleNode.getUncached().execute(null, obj); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + Object result = ConstructListNode.getUncached().execute(null, obj); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + Object value = NativeToPythonInternalNode.executeUncached(valuePtr, false); + 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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + if (PySequenceCheckNode.executeUncached(obj)) { + Object getItemCallable = PyObjectLookupAttr.executeUncached(obj, T___GETITEM__); + Object result = CallNode.executeUncached(getItemCallable, PySliceNew.executeUncached(iLow, iHigh, PNone.NONE)); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(haystackPtr, false); + Object needle = NativeToPythonInternalNode.executeUncached(needlePtr, false); + 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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + Object result = PySequenceInPlaceRepeatNode.executeUncached(obj, (int) n); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(s1Ptr, false); + Object s2 = NativeToPythonInternalNode.executeUncached(s2Ptr, false); + Object result = PySequenceConcatNode.executeUncached(s1, s2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(s1Ptr, false); + Object s2 = NativeToPythonInternalNode.executeUncached(s2Ptr, false); + Object result = PySequenceInPlaceConcatNode.executeUncached(s1, s2); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(oPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(delegatePtr, false); + Object result = PySequenceGetItemNode.executeUncached(delegate, (int) position); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(sequencePtr, false); + TpSlots slots = GetObjectSlotsNode.executeUncached(sequence); + if (slots.mp_ass_subscript() != null) { + Object s = NativeToPythonInternalNode.executeUncached(sPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(sequencePtr, false); + 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 = NativeToPythonInternalNode.executeUncached(haystackPtr, false); + Object needle = NativeToPythonInternalNode.executeUncached(needlePtr, false); + 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 = NativeToPythonInternalNode.executeUncached(haystackPtr, false); + Object needle = NativeToPythonInternalNode.executeUncached(needlePtr, false); + 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 = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PyObject_GetItem(long objPtr, long keyPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + Object key = NativeToPythonInternalNode.executeUncached(keyPtr, false); + Object result = PyObjectGetItem.executeUncached(obj, key); + return PythonToNativeInternalNode.executeNewRefUncached(result); } - @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(name = "GraalPyPrivate_Object_GetItemString", ret = PyObjectRawPointer, args = {PyObjectRawPointer, ConstCharPtr}, call = Ignored) + static long GraalPyPrivate_Object_GetItemString(long objPtr, long keyPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + Object key = CharPtrToPythonNode.executeUncached(keyPtr); + Object result = PyObjectGetItem.executeUncached(obj, key); + return PythonToNativeInternalNode.executeNewRefUncached(result); } - @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 = Py_ssize_t, args = {PyObjectRawPointer}, call = Ignored) + static long GraalPyPrivate_Object_Size(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + // 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); } - /////// 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); - } - - } - - 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 = 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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 PythonToNativeInternalNode.executeNewRefUncached(listResult); + } + + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct) + static long PyMapping_Items(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 PythonToNativeInternalNode.executeNewRefUncached(listResult); + } + + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct) + static long PyMapping_Values(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 PythonToNativeInternalNode.executeNewRefUncached(listResult); + } + + @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer}, call = Ignored) + static long GraalPyPrivate_Mapping_Size(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(iteratorPtr, false); + try { + Object result = PyIterNextNode.executeUncached(iterator); + if (result == NATIVE_NULL) { + return NULLPTR; } + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(iterPtr, false); + Object arg = NativeToPythonInternalNode.executeUncached(argPtr, false); + 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 PythonToNativeInternalNode.executeNewRefUncached(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 PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + Object value = CharPtrToPythonNode.executeUncached(valuePtr); + if (obj instanceof PBuiltinFunction builtinFunction) { + CFunctionDocUtils.writeDocAndTextSignature(builtinFunction, builtinFunction.getName(), 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(); + CFunctionDocUtils.writeDocAndTextSignature(builtinFunction, builtinFunction.getName(), 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..47fc8ebd6a 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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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, PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(arrayPtr, false); + 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..9789c30947 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,28 @@ 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.CExtNodes.EnsurePythonObjectNode; +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() { + Object trueValue = PythonContext.get(null).getTrue(); + assert EnsurePythonObjectNode.doesNotNeedPromotion(trueValue); + return PythonToNativeInternalNode.executeUncached(trueValue, 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() { + Object falseValue = PythonContext.get(null).getFalse(); + assert EnsurePythonObjectNode.doesNotNeedPromotion(falseValue); + return PythonToNativeInternalNode.executeUncached(falseValue, 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 c0bd0d172f..f1c9d2ed8f 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 @@ -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 @@ -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,22 @@ 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.annotations.CApiConstant; +import com.oracle.graal.python.annotations.NativeSimpleType; 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 +126,22 @@ 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.PythonToNativeInternalNode; +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 +149,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; @@ -154,16 +159,13 @@ 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; -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.str.PString; 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.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 +189,7 @@ 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.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.NativeByteSequenceStorage; @@ -194,19 +197,18 @@ 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; 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.Idempotent; import com.oracle.truffle.api.dsl.NonIdempotent; import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.Specialization; @@ -214,72 +216,16 @@ 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 { private static final TruffleLogger LOGGER = CApiContext.getLogger(PythonCextBuiltins.class); - /** - * Certain builtin types like {@code int} cannot handle refcounts. They cannot be handed out to - * the native side as borrowed references, since the handle would be collected immediately. (the - * boxed int, for example, is not referenced from anyhwere). This node promotes these types to - * full types like {@link PInt} and {@link PString}. - */ - @GenerateInline - @GenerateCached(false) - @GenerateUncached - public abstract static class PromoteBorrowedValue extends Node { - - public abstract Object execute(Node inliningTarget, Object value); - - @Specialization - static PString doString(TruffleString str, - @Bind PythonLanguage language) { - return PFactory.createString(language, str); - } - - @Specialization - static PythonBuiltinObject doInteger(int i, - @Bind PythonLanguage language) { - return PFactory.createInt(language, i); - } - - @Specialization - static PythonBuiltinObject doLong(long i, - @Bind PythonLanguage language) { - return PFactory.createInt(language, i); - } - - @Specialization(guards = "!isNaN(d)") - static PythonBuiltinObject doDouble(double d, - @Bind PythonLanguage language) { - return PFactory.createFloat(language, d); - } - - static boolean isNaN(double d) { - return Double.isNaN(d); - } - - @Fallback - static PythonBuiltinObject doOther(@SuppressWarnings("unused") Object value) { - return null; - } - } - public static PException checkThrowableBeforeNative(Throwable t, String where1, Object where2) { if (t instanceof PException pe) { // this is ok, and will be handled correctly @@ -322,28 +268,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 +301,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 +361,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 +528,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 +569,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 POINTER -> NULLPTR; + }; } CExtToJavaNode[] createArgNodes() { @@ -650,113 +592,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 +620,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 +672,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 +718,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 +728,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((Long) 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 +776,7 @@ public enum CApiCallPath { Ignored, } - @Target(ElementType.TYPE) + @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface CApiBuiltins { CApiBuiltin[] value(); @@ -928,7 +792,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 +821,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 +839,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 PythonToNativeInternalNode.executeNewRefUncached(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.executeUncached(typeNamePtr); + Python3Core core = PythonContext.get(null).getCore(); + for (PythonBuiltinClassType type : PythonBuiltinClassType.VALUES) { + if (type.getName().equalsUncached(typeName, TS_ENCODING)) { + return PythonToNativeInternalNode.executeNewRefUncached(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 PythonToNativeInternalNode.executeNewRefUncached(attribute); } - throw raiseNode.raise(inliningTarget, PythonErrorType.KeyError, ErrorMessages.APOSTROPHE_S, typeName); } + throw PRaiseNode.raiseStatic(null, PythonErrorType.KeyError, ErrorMessages.APOSTROPHE_S, typeName); } @GenerateInline @@ -1030,20 +892,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 +912,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) NativeToPythonInternalNode.executeUncached(codePtr, false); + PythonObject globals = (PythonObject) NativeToPythonInternalNode.executeUncached(globalsPtr, false); + Object locals = localsPtr == NULLPTR ? null : NativeToPythonInternalNode.executeUncached(localsPtr, false); + PythonLanguage language = PythonLanguage.get(null); + Object frameLocals = locals == null || PGuards.isPNone(locals) ? PFactory.createDict(language) : locals; + return PythonToNativeInternalNode.executeNewRefUncached(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 : NativeToPythonInternalNode.executeUncached(ownerPtr, false); + TruffleString format = (TruffleString) CharPtrToPythonNode.executeUncached(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}; + } + if (stridesPointer != NULLPTR) { + strides = readLongArrayElementsAsInts(stridesPointer, ndim); + } else { + strides = PMemoryView.initStridesFromShape(ndim, itemsize, shape); } - 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 (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 PythonToNativeInternalNode.executeNewRefUncached(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 +1068,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 +1093,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 +1201,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,133 +1299,109 @@ 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); + static int GraalPyPrivate_IsReferencedFromManaged(long lPointer) { + assert PythonContext.get(null).isNativeAccessAllowed(); + assert PythonLanguage.get(null).getEngineOption(PythonOptions.PythonGC); - long lPointer = coerceToLongNode.execute(inliningTarget, pointer); - // this upcall doesn't make sense for managed objects - assert !HandlePointerConverter.pointsToPyHandleSpace(lPointer); + // this upcall doesn't make sense for handle-space managed object stubs + assert !HandlePointerConverter.pointsToPyHandleSpace(lPointer); - Object object = gcNativePtrToPythonNode.execute(inliningTarget, lPointer); - return PInt.intValue(object != null); - } - - @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; - private static final int LOG_INFO = 0x2; - private static final int LOG_CONFIG = 0x4; - private static final int LOG_FINE = 0x8; - private static final int LOG_FINER = 0x10; - private static final int LOG_FINEST = 0x20; - private static final int DEBUG_CAPI = 0x40; - private static final int PYTHON_GC = 0x80; + @CApiConstant // + private static final int GRAALPY_TRACE_MEM = 0x1; + @CApiConstant // + private static final int GRAALPY_LOG_INFO = 0x2; + @CApiConstant // + private static final int GRAALPY_LOG_CONFIG = 0x4; + @CApiConstant // + private static final int GRAALPY_LOG_FINE = 0x8; + @CApiConstant // + private static final int GRAALPY_LOG_FINER = 0x10; + @CApiConstant // + private static final int GRAALPY_LOG_FINEST = 0x20; + @CApiConstant // + private static final int GRAALPY_DEBUG_CAPI = 0x40; + @CApiConstant // + private static final int GRAALPY_PYTHON_GC = 0x80; + @CApiConstant // + private static final int GRAALPY_POISON_NATIVE_MEMORY_ON_FREE = 0x100; + @CApiConstant // + private static final int GRAALPY_SAMPLE_NATIVE_MEMORY_ALLOC_SITES = 0x200; /* * These should be kept so they can be shared across multiple contexts in the same engine, if @@ -1651,130 +1410,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; - } - return options; + @TruffleBoundary + static int GraalPyPrivate_Native_Options() { + int options = 0; + PythonLanguage language = PythonLanguage.get(null); + if (language.getEngineOption(PythonOptions.TraceNativeMemory)) { + options |= GRAALPY_TRACE_MEM; + } + if (LOGGER.isLoggable(Level.INFO)) { + options |= GRAALPY_LOG_INFO; + } + if (LOGGER.isLoggable(Level.CONFIG)) { + options |= GRAALPY_LOG_CONFIG; } + if (LOGGER.isLoggable(Level.FINE)) { + options |= GRAALPY_LOG_FINE; + } + if (LOGGER.isLoggable(Level.FINER)) { + options |= GRAALPY_LOG_FINER; + } + if (LOGGER.isLoggable(Level.FINEST)) { + options |= GRAALPY_LOG_FINEST; + } + if (PythonContext.DEBUG_CAPI) { + options |= GRAALPY_DEBUG_CAPI; + } + if (language.getEngineOption(PythonOptions.PythonGC)) { + options |= GRAALPY_PYTHON_GC; + } + if (language.getEngineOption(PythonOptions.PoisonNativeMemoryOnFree)) { + options |= GRAALPY_POISON_NATIVE_MEMORY_ON_FREE; + } + if (language.getEngineOption(PythonOptions.SampleNativeMemoryAllocSites)) { + options |= GRAALPY_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.executeUncached(messagePtr); + String msg = message.toJavaStringUncached(); + switch (level) { + case GRAALPY_LOG_INFO: + LOGGER.info(msg); + break; + case GRAALPY_LOG_CONFIG: + LOGGER.config(msg); + break; + case GRAALPY_LOG_FINE: + LOGGER.fine(msg); + break; + case GRAALPY_LOG_FINER: + LOGGER.finer(msg); + break; + case GRAALPY_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; } /** @@ -1814,121 +1560,109 @@ 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); + @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); - // lookup the built-in type by name - PythonManagedClass clazz = lookupBuiltinTypeWithName(context, name); + // lookup the built-in type by name + PythonManagedClass clazz = lookupBuiltinTypeWithName(context, name); - // 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); + // 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); - builtinTypes.add(Pair.create(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)); - } + 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; - } - } - } - 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) NativeToPythonInternalNode.executeUncached(objectPtr, false); + PythonContext context = PythonContext.get(null); + try { + return PosixSupportLibrary.getUncached().mmapGetPointer(context.getPosixSupport(), object.getPosixSupportHandle()); + } catch (PosixSupportLibrary.UnsupportedPosixFeatureException e) { + throw PConstructAndRaiseNode.getUncached().raiseOSErrorUnsupported(null, e); } } + + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer}, call = Ignored) + static int GraalPyPrivate_MMap_IsReadonly(long objectPtr) { + PMMap object = (PMMap) NativeToPythonInternalNode.executeUncached(objectPtr, false); + return object.isWriteable() ? 0 : 1; + } } 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..5fad4c1e0c 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.NativeToPythonInternalNode; 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 = NativeToPythonInternalNode.executeUncached(bytesPtr, false); + 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..2932059be8 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.CApiTransitions.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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.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,40 @@ 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); + /* + * Moving this to native regresses median time by about 1.84x, so leaving it here. + */ + @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer}, call = Direct) + static long PyBytes_Size(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + if (obj instanceof PBytes bytes) { + return PyObjectSizeNode.executeUncached(bytes); } - - @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); - } - - @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 = NativeToPythonInternalNode.executeUncached(originalPtr, false); + Object newPart = NativeToPythonInternalNode.executeUncached(newPartPtr, false); + Object result = BytesCommonBuiltins.ConcatNode.executeUncached(original, newPart); + return PythonToNativeInternalNode.executeNewRefUncached(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 +137,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 +149,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 +166,102 @@ 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 PythonToNativeInternalNode.executeNewRefUncached(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 PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(selfPtr, false); + 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 PythonToNativeInternalNode.executeNewRefUncached(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); - } - } + @CApiBuiltin(ret = PyObjectRawPointer, call = Ignored) + static long GraalPyPrivate_Bytes_Empty() { + return PythonToNativeInternalNode.executeNewRefUncached(PFactory.createEmptyBytes(PythonLanguage.get(null))); + } - @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 PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(bytesPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(bytesPtr, false); + 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..f6fa50d2de 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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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) NativeToPythonInternalNode.executeUncached(codePtr, false); + PythonObject globals = (PythonObject) NativeToPythonInternalNode.executeUncached(globalsPtr, false); + Object locals = NativeToPythonInternalNode.executeUncached(localsPtr, false); + Object kwdefaultsWrapper = NativeToPythonInternalNode.executeUncached(kwdefaultsWrapperPtr, false); + Object closureObj = NativeToPythonInternalNode.executeUncached(closureObjPtr, false); + 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 PythonToNativeInternalNode.executeNewRefUncached(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..7219847e73 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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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 PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(oPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(oPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(oPtr, false); + 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..37ba4ef181 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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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 = NativeToPythonInternalNode.executeUncached(funcPtr, false); + checkNonNullArgUncached(func); + PDecoratedMethod res = PFactory.createInstancemethod(PythonLanguage.get(null)); + res.setCallable(func); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(funcPtr, false); + Object self = NativeToPythonInternalNode.executeUncached(selfPtr, false); + checkNonNullArgUncached(func); + checkNonNullArgUncached(self); + // Note: CPython also constructs the object directly, without running the constructor or + // checking the inputs. + return PythonToNativeInternalNode.executeNewRefUncached(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..5d6391ef4e 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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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 = NativeToPythonInternalNode.executeUncached(codePtr, false); + Object consts = NativeToPythonInternalNode.executeUncached(constsPtr, false); + Object names = NativeToPythonInternalNode.executeUncached(namesPtr, false); + Object varnames = NativeToPythonInternalNode.executeUncached(varnamesPtr, false); + Object freevars = NativeToPythonInternalNode.executeUncached(freevarsPtr, false); + Object cellvars = NativeToPythonInternalNode.executeUncached(cellvarsPtr, false); + Object filename = NativeToPythonInternalNode.executeUncached(filenamePtr, false); + Object name = NativeToPythonInternalNode.executeUncached(namePtr, false); + Object qualname = NativeToPythonInternalNode.executeUncached(qualnamePtr, false); + Object lnotab = NativeToPythonInternalNode.executeUncached(lnotabPtr, false); + Object exceptionTable = NativeToPythonInternalNode.executeUncached(exceptionTablePtr, false); + /* + * 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 PythonToNativeInternalNode.executeNewRefUncached(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.executeUncached(filenamePtr); + TruffleString funcname = (TruffleString) CharPtrToPythonNode.executeUncached(funcnamePtr); + return PythonToNativeInternalNode.executeNewRefUncached(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) NativeToPythonInternalNode.executeUncached(codePtr, false); + 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) NativeToPythonInternalNode.executeUncached(codePtr, false); + return PythonToNativeInternalNode.executeNewRefUncached(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) NativeToPythonInternalNode.executeUncached(codePtr, false); + return PythonToNativeInternalNode.executeNewRefUncached(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..0f1ad26316 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.PythonToNativeInternalNode; 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.executeUncached(encodingPtr); + PTuple codecInfo = CodecsModuleBuiltins.PyCodecLookupNode.executeUncached(encoding); + return PythonToNativeInternalNode.executeNewRefUncached(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.executeUncached(encodingPtr); + PTuple codecInfo = CodecsModuleBuiltins.PyCodecLookupNode.executeUncached(encoding); + return PythonToNativeInternalNode.executeNewRefUncached(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..975e7d3d28 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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(objPtr, false); + 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 PythonToNativeInternalNode.executeNewRefUncached(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..7eac1f8967 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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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.executeUncached(namePtr); + Object def = defPtr == NULLPTR ? PNone.NO_VALUE : NativeToPythonInternalNode.executeUncached(defPtr, false); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(varPtr, false); + 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 = NativeToPythonInternalNode.executeUncached(defPtr, false); } - return result; } + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(varPtr, false); + Object val = NativeToPythonInternalNode.executeUncached(valPtr, false); + 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 PythonToNativeInternalNode.executeNewRefUncached(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 PythonToNativeInternalNode.executeNewRefUncached(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) NativeToPythonInternalNode.executeUncached(contextPtr, false); + return PythonToNativeInternalNode.executeNewRefUncached(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 PythonToNativeInternalNode.executeNewRefUncached(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) NativeToPythonInternalNode.executeUncached(contextPtr, false); + 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) NativeToPythonInternalNode.executeUncached(contextPtr, false); + 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..81e1155a17 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___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.CExtNodes.EnsurePythonObjectNode; +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,28 @@ 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.executeUncached(nameRaw); + Object doc = CharPtrToPythonNode.executeUncached(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; + WriteAttributeToPythonObjectNode.executeUncached(func, T___NAME__, name); + CFunctionDocUtils.writeDocAndTextSignature(func, name, doc); + PDecoratedMethod classMethod = PFactory.createBuiltinClassmethodFromCallableObj(language, func); + WriteAttributeToPythonObjectNode.executeUncached(classMethod, T___NAME__, name); + HiddenAttr.WriteLongNode.executeUncached(classMethod, METHOD_DEF_PTR, methodDefPtr); + assert EnsurePythonObjectNode.doesNotNeedPromotion(classMethod); + 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..4cbba37438 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,18 +47,22 @@ 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; 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_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.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 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 java.util.logging.Level; @@ -67,15 +71,17 @@ import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi5BuiltinNode; 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.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.modules.cext.PythonCextBuiltins.PromoteBorrowedValue; 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.EnsurePythonObjectNode; +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.cext.capi.transitions.CApiTransitions.GcNativePtrToPythonNode; +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.DynamicObjectStorage; import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.SetItemNode; @@ -102,6 +108,7 @@ import com.oracle.graal.python.builtins.objects.dict.PDict; 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.object.PythonObject; import com.oracle.graal.python.builtins.objects.str.StringBuiltins; import com.oracle.graal.python.lib.PyDictDelItem; import com.oracle.graal.python.lib.PyDictSetDefault; @@ -112,10 +119,13 @@ import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode; +import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; 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,22 +136,26 @@ 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.object.DynamicObject; 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 { + private static final CApiTiming TIMING_PYDICT_NEW = CApiTiming.create(false, "PyDict_New"); - @Specialization - static Object run( - @Bind PythonLanguage language) { - return PFactory.createDict(language); + @CApiBuiltin(ret = PyObjectTransfer, args = {}, call = Direct, acquireGil = false) + static long PyDict_New() { + CApiTiming.enter(); + try { + PDict dict = PFactory.createDict(PythonLanguage.get(null)); + assert EnsurePythonObjectNode.doesNotNeedPromotion(dict); + return PythonToNativeInternalNode.executeUncached(dict, true); + } finally { + CApiTiming.exit(TIMING_PYDICT_NEW); } } @@ -149,13 +163,10 @@ 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, + @Bind PythonContext context, + @Cached CApiTransitions.PythonToNativeInternalNode toNativeNode, @Cached InlinedBranchProfile needsRewriteProfile, @Cached InlinedBranchProfile economicMapProfile, @Cached HashingStorageLen lenNode, @@ -164,16 +175,17 @@ static int next(PDict dict, Object posPtr, Object keyPtr, Object valuePtr, Objec @Cached HashingStorageIteratorKey itKey, @Cached HashingStorageIteratorValue itValue, @Cached HashingStorageIteratorKeyHash itKeyHash, - @Cached PromoteBorrowedValue promoteKeyNode, - @Cached PromoteBorrowedValue promoteValueNode, - @Cached HashingStorageSetItem setItem) { + @Cached EnsurePythonObjectNode ensureKeyNode, + @Cached EnsurePythonObjectNode ensureValueNode, + @Cached HashingStorageSetItem setItem, + @Cached DynamicObject.SetShapeFlagsNode setShapeFlagsNode) { /* * We need to promote primitive values and strings to object types for borrowing to work * correctly. This is very hard to do mid-iteration, so we do all the promotion for the * 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); @@ -183,8 +195,10 @@ static int next(PDict dict, Object posPtr, Object keyPtr, Object valuePtr, Objec economicMapProfile.enter(inliningTarget); HashingStorageIterator it = getIterator.execute(inliningTarget, storage); while (itNext.execute(inliningTarget, storage, it)) { - if (promoteKeyNode.execute(inliningTarget, itKey.execute(inliningTarget, storage, it)) != null || - promoteValueNode.execute(inliningTarget, itValue.execute(inliningTarget, storage, it)) != null) { + Object key = itKey.execute(inliningTarget, storage, it); + Object value = itValue.execute(inliningTarget, storage, it); + if (ensureKeyNode.execute(context, key, false) != key || + ensureValueNode.execute(context, value, false) != value) { needsRewrite = true; break; } @@ -203,18 +217,22 @@ static int next(PDict dict, Object posPtr, Object keyPtr, Object valuePtr, Objec while (itNext.execute(inliningTarget, storage, it)) { Object key = itKey.execute(inliningTarget, storage, it); Object value = itValue.execute(inliningTarget, storage, it); - Object promotedKey = promoteKeyNode.execute(inliningTarget, key); - if (promotedKey != null) { + Object promotedKey = ensureKeyNode.execute(context, key, false); + if (promotedKey != key) { key = promotedKey; } - Object promotedValue = promoteValueNode.execute(inliningTarget, value); - if (promotedValue != null) { + Object promotedValue = ensureValueNode.execute(context, value, false); + if (promotedValue != value) { value = promotedValue; } // promoted key will never have side-effecting __hash__/__eq__ setItem.execute(null, inliningTarget, newStorage, key, value); } dict.setDictStorage(newStorage); + if (storage instanceof DynamicObjectStorage dynamicStorage && + dynamicStorage.getStore() instanceof PythonObject owner) { + setShapeFlagsNode.executeAdd(owner, PythonObject.HAS_MATERIALIZED_DICT); + } } } } @@ -231,22 +249,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; + assert ensureKeyNode.execute(context, key, false) == key; // Borrowed reference - writePointerNode.write(keyPtr, toNativeNode.execute(key)); + NativeMemory.writePtr(keyPtr, toNativeNode.execute(inliningTarget, key)); } - if (!lib.isNull(valuePtr)) { + if (valuePtr != NULLPTR) { Object value = itValue.execute(inliningTarget, storage, it); - assert promoteValueNode.execute(inliningTarget, value) == null; + assert ensureValueNode.execute(context, value, false) == value; // Borrowed reference - writePointerNode.write(valuePtr, toNativeNode.execute(value)); + NativeMemory.writePtr(valuePtr, toNativeNode.execute(inliningTarget, 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; } @@ -272,21 +290,6 @@ 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, - @Bind Node inliningTarget, - @Cached HashingStorageLen lenNode) { - return lenNode.execute(inliningTarget, dict.getDictStorage()); - } - - @Fallback - public int fallback(Object dict) { - throw raiseFallback(dict, PythonBuiltinClassType.PDict); - } - } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) abstract static class PyDict_Copy extends CApiUnaryBuiltinNode { @Specialization @@ -309,25 +312,26 @@ public abstract static class PyDict_GetItem extends CApiBinaryBuiltinNode { @Specialization static Object getItem(PDict dict, Object key, @Bind Node inliningTarget, + @Bind PythonContext context, @Cached HashingStorageGetItem getItem, - @Cached PromoteBorrowedValue promoteNode, + @Cached EnsurePythonObjectNode ensureNode, @Cached SetItemNode setItemNode, @Cached InlinedBranchProfile noResultProfile) { try { 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) { + Object promotedValue = ensureNode.execute(context, res, false); + if (promotedValue != res) { setItemNode.execute(null, inliningTarget, dict, key, promotedValue); return promotedValue; } return res; } catch (PException e) { // PyDict_GetItem suppresses all exceptions for historical reasons - return getNativeNull(inliningTarget); + return NATIVE_NULL; } } @@ -348,17 +352,18 @@ abstract static class PyDict_GetItemWithError extends CApiBinaryBuiltinNode { @Specialization static Object getItem(PDict dict, Object key, @Bind Node inliningTarget, + @Bind PythonContext context, @Cached HashingStorageGetItem getItem, - @Cached PromoteBorrowedValue promoteNode, + @Cached EnsurePythonObjectNode ensureNode, @Cached SetItemNode setItemNode, @Cached InlinedBranchProfile noResultProfile) { 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) { + Object promotedValue = ensureNode.execute(context, res, false); + if (promotedValue != res) { setItemNode.execute(null, inliningTarget, dict, key, promotedValue); return promotedValue; } @@ -418,8 +423,17 @@ abstract static class PyDict_SetDefault extends CApiTernaryBuiltinNode { @Specialization static Object setItem(PDict dict, Object key, Object value, @Bind Node inliningTarget, - @Cached PyDictSetDefault setDefault) { - return setDefault.execute(null, inliningTarget, dict, key, value); + @Bind PythonContext context, + @Cached PyDictSetDefault setDefault, + @Cached EnsurePythonObjectNode ensureNode, + @Cached SetItemNode setItemNode) { + Object result = setDefault.execute(null, inliningTarget, dict, key, value); + Object promotedValue = ensureNode.execute(context, result, false); + if (promotedValue != result) { + setItemNode.execute(null, inliningTarget, dict, key, promotedValue); + return promotedValue; + } + return result; } @Fallback @@ -647,12 +661,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 +676,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 +716,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 e70d332410..c032befcfd 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 @@ -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 @@ -61,11 +61,11 @@ 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; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions.IsSubClassNode; import com.oracle.graal.python.builtins.modules.PosixModuleBuiltins.ExitNode; import com.oracle.graal.python.builtins.modules.SysModuleBuiltins; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; @@ -94,12 +94,15 @@ import com.oracle.graal.python.lib.PyLongCheckNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetAttr; +import com.oracle.graal.python.lib.PyObjectIsSubclassNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectSetAttr; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.WriteUnraisableNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; +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; @@ -127,8 +130,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 +159,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 +168,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); @@ -197,7 +200,7 @@ abstract static class GraalPyPrivate_Err_CreateAndSetException extends CApiBinar @Specialization(guards = "!isExceptionClass(inliningTarget, type, isTypeNode, isSubClassNode)") static Object create(Object type, @SuppressWarnings("unused") Object value, @SuppressWarnings("unused") @Shared @Cached IsTypeNode isTypeNode, - @SuppressWarnings("unused") @Shared @Cached IsSubClassNode isSubClassNode, + @SuppressWarnings("unused") @Shared @Cached PyObjectIsSubclassNode isSubClassNode, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.SystemError, EXCEPTION_NOT_BASEEXCEPTION, new Object[]{type}); } @@ -206,14 +209,14 @@ static Object create(Object type, @SuppressWarnings("unused") Object value, static Object create(Object type, Object value, @Bind Node inliningTarget, @SuppressWarnings("unused") @Shared @Cached IsTypeNode isTypeNode, - @SuppressWarnings("unused") @Shared @Cached IsSubClassNode isSubClassNode, + @SuppressWarnings("unused") @Shared @Cached PyObjectIsSubclassNode isSubClassNode, @Cached PrepareExceptionNode prepareExceptionNode) { Object exception = prepareExceptionNode.execute(null, type, value); throw PRaiseNode.raiseExceptionObjectStatic(inliningTarget, exception); } - protected static boolean isExceptionClass(Node inliningTarget, Object obj, IsTypeNode isTypeNode, IsSubClassNode isSubClassNode) { - return isTypeNode.execute(inliningTarget, obj) && isSubClassNode.executeWith(null, obj, PythonBuiltinClassType.PBaseException); + protected static boolean isExceptionClass(Node inliningTarget, Object obj, IsTypeNode isTypeNode, PyObjectIsSubclassNode isSubClassNode) { + return isTypeNode.execute(inliningTarget, obj) && isSubClassNode.execute(null, obj, PythonBuiltinClassType.PBaseException); } } @@ -225,7 +228,7 @@ static Object newEx(TruffleString name, Object base, Object dict, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached HashingStorageGetItem getItem, - @Cached TruffleString.IndexOfCodePointNode indexOfCodepointNode, + @Cached TruffleString.LastIndexOfCodePointNode lastIndexOfCodepointNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.SubstringNode substringNode, @Cached PyDictSetItem setItemNode, @@ -233,6 +236,8 @@ static Object newEx(TruffleString name, Object base, Object dict, @Cached InlinedBranchProfile notDotProfile, @Cached InlinedBranchProfile notModuleProfile, @Cached InlinedConditionProfile baseProfile, + @Cached PyTupleCheckNode tupleCheck, + @Cached ConstructTupleNode constructTupleNode, @Cached PRaiseNode raiseNode) { if (base == PNone.NO_VALUE) { base = PythonErrorType.Exception; @@ -241,18 +246,18 @@ static Object newEx(TruffleString name, Object base, Object dict, dict = PFactory.createDict(language); } int length = codePointLengthNode.execute(name, TS_ENCODING); - int dotIdx = indexOfCodepointNode.execute(name, '.', 0, length, TS_ENCODING); + int dotIdx = lastIndexOfCodepointNode.execute(name, '.', length, 0, TS_ENCODING); if (dotIdx < 0) { notDotProfile.enter(inliningTarget); throw raiseNode.raise(inliningTarget, SystemError, MUST_BE_MODULE_CLASS, "PyErr_NewException", "name"); } - if (getItem.execute(null, inliningTarget, ((PDict) dict).getDictStorage(), base) == null) { + if (getItem.execute(null, inliningTarget, ((PDict) dict).getDictStorage(), T___MODULE__) == null) { notModuleProfile.enter(inliningTarget); setItemNode.execute(null, inliningTarget, (PDict) dict, T___MODULE__, substringNode.execute(name, 0, dotIdx, TS_ENCODING, false)); } PTuple bases; - if (baseProfile.profile(inliningTarget, base instanceof PTuple)) { - bases = (PTuple) base; + if (baseProfile.profile(inliningTarget, tupleCheck.execute(inliningTarget, base))) { + bases = constructTupleNode.execute(null, base); } else { bases = PFactory.createTuple(language, new Object[]{base}); } @@ -297,11 +302,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 +315,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 +459,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 +469,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 +491,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/PythonCextFuncBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFuncBuiltins.java index f913868ba0..548a0420d6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFuncBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFuncBuiltins.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,7 +45,6 @@ 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.PyObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer; -import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; @@ -108,7 +107,8 @@ private static PNone setDoc(Object functionObj, TruffleString doc) { } else { throw CompilerDirectives.shouldNotReachHere("Unexpected object passed to GraalPyCFunction_SetDoc"); } - function.setAttribute(T___DOC__, doc != null ? doc : PNone.NONE); + CFunctionDocUtils.writeDocAndTextSignature(function, function.getName(), + doc != null ? doc : PNone.NO_VALUE); return PNone.NO_VALUE; } } 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/PythonCextIterBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextIterBuiltins.java index d2f60fba14..3072377989 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextIterBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextIterBuiltins.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,7 +41,6 @@ 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.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.PyObjectTransfer; @@ -51,25 +50,13 @@ 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.iterator.PSequenceIterator; -import com.oracle.graal.python.lib.PyIterCheckNode; 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 PythonCextIterBuiltins { - @CApiBuiltin(ret = Int, args = {PyObject}, call = Direct) - abstract static class PyIter_Check extends CApiUnaryBuiltinNode { - @Specialization - static int check(Object obj, - @Bind Node inliningTarget, - @Cached PyIterCheckNode check) { - return check.execute(inliningTarget, obj) ? 1 : 0; - } - } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) abstract static class PySeqIter_New extends CApiUnaryBuiltinNode { @Specialization 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..a5407f73b9 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; @@ -63,12 +64,11 @@ 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.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.capi.CExtNodes.EnsurePythonObjectNode; 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; @@ -82,6 +82,7 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.ListNodes.AppendNode; import com.oracle.graal.python.nodes.builtins.TupleNodes.ConstructTupleNode; +import com.oracle.graal.python.runtime.PythonContext; 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.ObjectSequenceStorage; @@ -128,7 +129,8 @@ abstract static class PyList_GetItem extends CApiBinaryBuiltinNode { @Specialization static Object doPList(PList list, long key, @Bind Node inliningTarget, - @Cached PromoteBorrowedValue promoteNode, + @Bind PythonContext context, + @Cached EnsurePythonObjectNode ensureNode, @Cached ListGeneralizationNode generalizationNode, @Cached SetItemScalarNode setItemNode, @Cached GetItemScalarNode getItemNode, @@ -139,8 +141,8 @@ static Object doPList(PList list, long key, throw raiseNode.raise(inliningTarget, IndexError, ErrorMessages.LIST_INDEX_OUT_OF_RANGE); } Object result = getItemNode.execute(inliningTarget, sequenceStorage, (int) key); - Object promotedValue = promoteNode.execute(inliningTarget, result); - if (promotedValue != null) { + Object promotedValue = ensureNode.execute(context, result, false); + if (promotedValue != result) { sequenceStorage = generalizationNode.execute(inliningTarget, sequenceStorage, promotedValue); list.setSequenceStorage(sequenceStorage); setItemNode.execute(inliningTarget, sequenceStorage, (int) key, promotedValue); @@ -239,16 +241,20 @@ Object fallback(Object list, @SuppressWarnings("unused") Object iterable) { } } + /* + * A pure-C Py_SIZE implementation regressed mixed managed/native/list-subclass workload by + * about 1.26x. + */ @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Direct) 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 +311,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 +341,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 +350,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 6e994214b4..da4a136c8f 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 @@ -44,23 +44,21 @@ 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.CONST_UNSIGNED_CHAR_PTR; -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.ConstPyLongObject; +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.LONG_LONG; 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.PyLongObject; 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.capi.transitions.ArgDescriptor.SIZE_T; 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; - import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi5BuiltinNode; @@ -69,19 +67,20 @@ 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.CApiTransitions.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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.CExtCommonNodes.TransformPExceptionToNativeCachedNode; import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.ConvertPIntToPrimitiveNodeGen; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; 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; @@ -91,16 +90,12 @@ 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.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; @@ -108,97 +103,6 @@ public final class PythonCextLongBuiltins { - @CApiBuiltin(ret = Int, args = {PyObject}, call = Direct) - abstract static class _PyLong_Sign extends CApiUnaryBuiltinNode { - - @SuppressWarnings("unused") - @Specialization(guards = "n == 0") - static int sign(int n) { - return 0; - } - - @SuppressWarnings("unused") - @Specialization(guards = "n < 0") - static int signNeg(int n) { - return -1; - } - - @SuppressWarnings("unused") - @Specialization(guards = "n > 0") - static int signPos(int n) { - return 1; - } - - @SuppressWarnings("unused") - @Specialization(guards = "n == 0") - static int sign(long n) { - return 0; - } - - @SuppressWarnings("unused") - @Specialization(guards = "n < 0") - static int signNeg(long n) { - return -1; - } - - @SuppressWarnings("unused") - @Specialization(guards = "n > 0") - static int signPos(long n) { - return 1; - } - - @SuppressWarnings("unused") - @Specialization(guards = "b") - static int signTrue(boolean b) { - return 1; - } - - @SuppressWarnings("unused") - @Specialization(guards = "!b") - static int signFalse(boolean b) { - return 0; - } - - @Specialization - static int sign(PInt n, - @Bind Node inliningTarget, - @Cached InlinedBranchProfile zeroProfile, - @Cached InlinedBranchProfile negProfile) { - if (n.isNegative()) { - negProfile.enter(inliningTarget); - return -1; - } else if (n.isZero()) { - zeroProfile.enter(inliningTarget); - return 0; - } else { - return 1; - } - } - - @SuppressWarnings("unused") - @Specialization(guards = {"!canBeInteger(obj)", "isPIntSubtype(inliningTarget, obj, getClassNode, isSubtypeNode)"}) - static Object signNative(Object obj, - @Bind Node inliningTarget, - @Shared @Cached GetClassNode getClassNode, - @Shared @Cached IsSubtypeNode isSubtypeNode) { - // function returns int, but -1 is expected result for 'n < 0' - throw CompilerDirectives.shouldNotReachHere("not yet implemented"); - } - - @Specialization(guards = {"!isInteger(obj)", "!isPInt(obj)", "!isPIntSubtype(inliningTarget, obj,getClassNode,isSubtypeNode)"}) - static Object sign(@SuppressWarnings("unused") Object obj, - @Bind Node inliningTarget, - @SuppressWarnings("unused") @Shared @Cached GetClassNode getClassNode, - @SuppressWarnings("unused") @Shared @Cached IsSubtypeNode isSubtypeNode) { - // assert(PyLong_Check(v)); - throw CompilerDirectives.shouldNotReachHere(); - } - - protected boolean isPIntSubtype(Node inliningTarget, Object obj, GetClassNode getClassNode, IsSubtypeNode isSubtypeNode) { - return isSubtypeNode.execute(getClassNode.execute(inliningTarget, obj), PythonBuiltinClassType.PInt); - } - } - @CApiBuiltin(ret = Py_ssize_t, args = {PyLongObject}, call = Ignored) abstract static class GraalPyPrivate_Long_DigitCount extends CApiUnaryBuiltinNode { @@ -210,18 +114,13 @@ static long getDC(Object n, } } - @CApiBuiltin(ret = PyObjectTransfer, args = {ArgDescriptor.Double}, call = Direct) - abstract static class PyLong_FromDouble extends CApiUnaryBuiltinNode { - - @Specialization - static Object fromDouble(double d, - @Bind Node inliningTarget, - @Cached PyLongFromDoubleNode pyLongFromDoubleNode) { - return pyLongFromDoubleNode.execute(inliningTarget, d); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {ArgDescriptor.Double}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_Long_FromDouble(double d) { + Object result = PyLongFromDoubleNode.executeUncached(d); + return PythonToNativeInternalNode.executeNewRefUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {CharPtrAsTruffleString, Int}, call = Ignored) + @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtrAsTruffleString, Int}, call = Ignored) abstract static class GraalPyPrivate_Long_FromString extends CApiBinaryBuiltinNode { @Specialization @@ -232,20 +131,6 @@ Object fromString(Object s, int base, } } - @CApiBuiltin(ret = Int, args = {ConstPyLongObject}, call = Direct) - abstract static class PyUnstable_Long_IsCompact extends CApiUnaryBuiltinNode { - @Specialization - @TruffleBoundary - static int doI(Object value) { - if (value instanceof Integer || value instanceof Long) { - return 1; - } else if (value instanceof PInt pInt) { - return pInt.fitsIn(PInt.MIN_LONG, PInt.MAX_LONG) ? 1 : 0; - } - return 0; - } - } - @CApiBuiltin(ret = LONG_LONG, args = {PyObject, Int, SIZE_T}, call = Ignored) abstract static class GraalPyPrivate_Long_AsPrimitive extends CApiTernaryBuiltinNode { @@ -293,68 +178,31 @@ 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) - @CApiBuiltin(name = "PyLong_FromUnsignedLong", ret = PyObjectTransfer, args = {UNSIGNED_LONG}, call = Direct) - @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; - } - - @Specialization(guards = "n < 0") - static Object doUnsignedLongNegative(long n, - @Bind PythonLanguage language) { - 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); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {UNSIGNED_LONG_LONG}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_Long_FromUnsignedLongLong(long n) { + Object result = n >= 0 ? n : PFactory.createInt(PythonLanguage.get(null), PInt.longToUnsignedBigInteger(n)); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } - @TruffleBoundary - private static BigInteger convertToBigInteger(long n) { - return BigInteger.valueOf(n).add(BigInteger.ONE.shiftLeft(Long.SIZE)); - } + @CApiBuiltin(ret = SIZE_T, args = {PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_Long_NumBits(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + if (obj instanceof Integer value) { + return Integer.SIZE - Integer.numberOfLeadingZeros(Math.abs(value)); + } else if (obj instanceof Long value) { + return Long.SIZE - Long.numberOfLeadingZeros(Math.abs(value)); + } else if (obj instanceof PInt value) { + return value.bitLength(); + } else if (obj instanceof Boolean value) { + return value ? 1 : 0; + } + throw CompilerDirectives.shouldNotReachHere(); } - @CApiBuiltin(ret = Pointer, args = {PyObject}, call = Direct) - public abstract static class PyLong_AsVoidPtr extends CApiUnaryBuiltinNode { + @CApiBuiltin(ret = Pointer, args = {PyObject}, call = Ignored) + abstract static class GraalPyPrivate_Long_AsVoidPtr extends CApiUnaryBuiltinNode { @Child private ConvertPIntToPrimitiveNode asPrimitiveNode; @Child private TransformPExceptionToNativeCachedNode transformExceptionToNativeNode; @@ -376,21 +224,15 @@ long doPointer(PInt n, try { return n.longValueExact(); } catch (OverflowException e) { - overflowProfile.enter(inliningTarget); - try { - throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO, "C long"); - } catch (PException pe) { - ensureTransformExcNode().execute(pe); - return 0; + if (!n.isNegative() && n.bitLength() <= Long.SIZE) { + return n.longValue(); } + overflowProfile.enter(inliningTarget); + transformOverflow(inliningTarget, raiseNode); + return 0; } } - @Specialization - static Object doPointer(PythonNativeVoidPtr n) { - return n.getPointerObject(); - } - @Fallback long doGeneric(Object n, @Bind Node inliningTarget, @@ -403,7 +245,8 @@ long doGeneric(Object n, try { return asPrimitiveNode.executeLongCached(n, 0, Long.BYTES); } catch (UnexpectedResultException e) { - throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO, "C long"); + transformOverflow(inliningTarget, raiseNode); + return 0; } } catch (PException e) { ensureTransformExcNode().execute(e); @@ -411,6 +254,14 @@ long doGeneric(Object n, } } + private void transformOverflow(Node inliningTarget, PRaiseNode raiseNode) { + try { + throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO, "C long"); + } catch (PException pe) { + ensureTransformExcNode().execute(pe); + } + } + private TransformPExceptionToNativeCachedNode ensureTransformExcNode() { if (transformExceptionToNativeNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -431,38 +282,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,25 +328,16 @@ 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); } } - @CApiBuiltin(ret = SIZE_T, args = {PyObject}, call = Direct) - abstract static class _PyLong_NumBits extends CApiUnaryBuiltinNode { - @Specialization - static long numBits(Object obj, - @Cached IntBuiltins.BitLengthNode bitLengthNode) { - return bitLengthNode.execute(obj); - } - } } 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..896dcaf04e 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,25 @@ 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.nodes.SpecialAttributeNames.T___DOC__; +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___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.CExtNodes.EnsurePythonObjectNode; +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.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 +78,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); + CFunctionDocUtils.writeDocAndTextSignature(func, name, doc); + PBuiltinMethod method; + if (cls != PNone.NO_VALUE) { + method = PFactory.createBuiltinMethod(language, self, func, cls); + } else { + method = PFactory.createBuiltinMethod(language, self, func); + } + 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.executeUncached(nameRaw); + Object self = NativeToPythonInternalNode.executeUncached(selfRaw, false); + Object module = NativeToPythonInternalNode.executeUncached(moduleRaw, false); + Object cls = NativeToPythonInternalNode.executeUncached(clsRaw, false); + Object doc = CharPtrToPythonNode.executeUncached(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); + assert EnsurePythonObjectNode.doesNotNeedPromotion(result); + 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 57fca05976..7731e3ab25 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 @@ -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,41 +47,52 @@ 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.CExtNodes.EnsurePythonObjectNode; +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.PythonToNativeNode; -import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.EnsureExecutableNode; +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.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 +102,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 +112,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; @@ -169,7 +178,8 @@ abstract static class PyModule_GetNameObject extends CApiUnaryBuiltinNode { @Specialization static Object getName(PythonModule module, @Bind Node inliningTarget, - @Cached PythonCextBuiltins.PromoteBorrowedValue promoteBorrowedValue, + @Bind PythonContext context, + @Cached EnsurePythonObjectNode ensureNode, @Cached PyUnicodeCheckNode pyUnicodeCheckNode, // CPython reads from the module dict directly @Cached ReadAttributeFromModuleNode read, @@ -184,8 +194,8 @@ static Object getName(PythonModule module, CompilerDirectives.transferToInterpreterAndInvalidate(); throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.NAMELESS_MODULE); } - Object promotedName = promoteBorrowedValue.execute(inliningTarget, nameAttr); - if (promotedName == null) { + Object promotedName = ensureNode.execute(context, nameAttr, false); + if (promotedName == nameAttr) { return nameAttr; } else { write.execute(module, T___NAME__, promotedName); @@ -243,59 +253,83 @@ 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 ReadAttributeFromPythonObjectNode readAttrNode, - @Cached CFunctionNewExMethodNode cFunctionNewExMethodNode) { - Object modName = readAttrNode.execute(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.executeUncached(nameRaw); + Object doc = CharPtrToPythonNode.executeUncached(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) { + @Cached PythonToNativeInternalNode toNativeNode) { /* * 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.execute(inliningTarget, self), visitFun, arg); + checkPrimitiveFunctionResultNode.executeLong(inliningTarget, ctx.getThreadState(PythonLanguage.get(inliningTarget)), StringLiterals.T_VISIT, ires); + return ires; } } } @@ -328,7 +362,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 +371,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 573f9f4b51..736485da88 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 @@ -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,26 +51,24 @@ 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.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.runtime.nativeaccess.NativeMemory.readPtrArrayElement; 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; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions.FormatNode; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions.IsInstanceNode; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions.IsSubClassNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi5BuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin; @@ -85,17 +83,14 @@ 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.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.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.PythonToNativeInternalNode; +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,29 +99,33 @@ 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.tuple.PTuple; +import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyBytesCheckNode; -import com.oracle.graal.python.lib.PyCallableCheckNode; import com.oracle.graal.python.lib.PyLongCheckNode; import com.oracle.graal.python.lib.PyObjectAsFileDescriptor; -import com.oracle.graal.python.lib.PyObjectAsciiNode; +import com.oracle.graal.python.lib.PyObjectAsciiAsObjectNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectDelItem; import com.oracle.graal.python.lib.PyObjectDir; +import com.oracle.graal.python.lib.PyObjectFormat; import com.oracle.graal.python.lib.PyObjectGetAttrO; import com.oracle.graal.python.lib.PyObjectGetIter; import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyObjectIsInstanceNode; +import com.oracle.graal.python.lib.PyObjectIsSubclassNode; import com.oracle.graal.python.lib.PyObjectIsTrueNode; import com.oracle.graal.python.lib.PyObjectLookupAttrO; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; import com.oracle.graal.python.lib.PyObjectSetItem; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.BuiltinNames; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.argument.keywords.ExpandKeywordStarargsNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode; @@ -145,15 +144,13 @@ 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.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; @@ -165,17 +162,33 @@ public abstract class PythonCextObjectBuiltins { private PythonCextObjectBuiltins() { } - @CApiBuiltin(ret = Void, args = {PyObjectWrapper, Py_ssize_t}, call = Ignored) - abstract static class GraalPyPrivate_NotifyRefCount extends CApiBinaryBuiltinNode { - @Specialization - static Object doGeneric(PythonAbstractObjectNativeWrapper wrapper, long refCount, - @Bind Node inliningTarget, - @Cached UpdateStrongRefNode updateRefNode) { - assert CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(wrapper.getNativePointer())) == refCount; + private static final CApiTiming TIMING_GRAALPYPRIVATE_NOTIFYREFCOUNT = CApiTiming.create(false, "GraalPyPrivate_NotifyRefCount"); + + @CApiBuiltin(ret = Void, args = {Pointer, Py_ssize_t}, call = Ignored, acquireGil = false, canRaise = false) + static void GraalPyPrivate_NotifyRefCount(long pointer, long refCount) { + assert PythonContext.get(null).ownsGil(); + CApiTiming.enter(); + try { + 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); - return PNone.NO_VALUE; + assert refCount != PythonObject.IMMORTAL_REFCNT; + HandleContext handleContext = PythonContext.get(null).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) { + UpdateHandleTableReferenceNode.executeUncached(handleContext, pointer, hti, refCount); + } + } finally { + CApiTiming.exit(TIMING_GRAALPYPRIVATE_NOTIFYREFCOUNT); } } @@ -183,34 +196,46 @@ 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); } + 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; } + } @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, PyObject, Int}, call = Ignored) @@ -233,66 +258,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 { + @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, PyObjectConstPtr, Py_ssize_t}, call = Ignored) + abstract static class GraalPyPrivate_Object_CallMethodObjArgs extends CApiQuaternaryBuiltinNode { @Specialization - static Object doFunction(Object callable, 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 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 { - - @Specialization - static Object doMethod(Object receiver, Object methodName, Object vaList, - @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,13 +297,15 @@ 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, @Cached ExpandKeywordStarargsNode castKwargsNode, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, @Cached CallNode callNode, + @Cached PyTupleCheckNode tupleCheck, + @Cached GetTupleStorage getTupleStorage, @Cached CastToTruffleStringNode castToTruffleStringNode) { try { @@ -335,10 +315,9 @@ static Object doGeneric(@SuppressWarnings("unused") Object threadState, Object c keywords = PKeyword.EMPTY_KEYWORDS; } else if (kwargs instanceof PDict) { keywords = castKwargsNode.execute(inliningTarget, kwargs); - } else if (kwargs instanceof PTuple) { + } else if (tupleCheck.execute(inliningTarget, kwargs)) { // We have a tuple with kw names and an array with kw values - PTuple kwTuple = (PTuple) kwargs; - SequenceStorage storage = kwTuple.getSequenceStorage(); + SequenceStorage storage = getTupleStorage.execute(inliningTarget, kwargs); int kwcount = storage.length(); Object[] kwValues = readKwNode.readPyObjectArray(argsArray, kwcount, (int) nargs); keywords = new PKeyword[kwcount]; @@ -414,8 +393,8 @@ static Object doGeneric(Object obj, Object k, Object v, abstract static class PyObject_IsInstance extends CApiBinaryBuiltinNode { @Specialization static int doGeneric(Object obj, Object typ, - @Cached IsInstanceNode isInstanceNode) { - return intValue((boolean) isInstanceNode.execute(null, obj, typ)); + @Cached PyObjectIsInstanceNode isInstanceNode) { + return intValue(isInstanceNode.execute(null, obj, typ)); } } @@ -423,8 +402,8 @@ static int doGeneric(Object obj, Object typ, abstract static class PyObject_IsSubclass extends CApiBinaryBuiltinNode { @Specialization static int doGeneric(Object obj, Object typ, - @Cached IsSubClassNode isSubclassNode) { - return intValue((boolean) isSubclassNode.execute(null, obj, typ)); + @Cached PyObjectIsSubclassNode isSubclassNode) { + return intValue(isSubclassNode.execute(null, obj, typ)); } } @@ -488,22 +467,23 @@ static int hasAttr(Object obj, Object attr, } } + /* + * Moving this to pure-C regresses, because creating the TypeError through the C error API is + * slower than upcalling and raising from Java. + */ @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); } } - @CApiBuiltin(ret = Int, args = {PyObject}, call = Direct) - abstract static class PyObject_IsTrue extends CApiUnaryBuiltinNode { - @Specialization - static int isTrue(Object obj, - @Cached PyObjectIsTrueNode isTrueNode) { - return isTrueNode.execute(null, obj) ? 1 : 0; - } + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer}, call = Ignored) + static int GraalPyPrivate_Object_IsTrue(long objPtr) { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + return PyObjectIsTrueNode.executeUncached(obj) ? 1 : 0; } @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) @@ -601,54 +581,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 +636,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 @@ -669,9 +649,9 @@ int doGeneric(Object ptrObject, @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) abstract static class PyObject_ASCII extends CApiUnaryBuiltinNode { @Specialization(guards = "!isNoValue(obj)") - static TruffleString ascii(Object obj, + static Object ascii(Object obj, @Bind Node inliningTarget, - @Cached PyObjectAsciiNode asciiNode) { + @Cached PyObjectAsciiAsObjectNode asciiNode) { return asciiNode.execute(null, inliningTarget, obj); } @@ -685,21 +665,22 @@ static TruffleString asciiNone(@SuppressWarnings("unused") PNone obj) { abstract static class PyObject_Format extends CApiBinaryBuiltinNode { @Specialization static Object ascii(Object obj, Object spec, - @Cached FormatNode format) { + @Cached PyObjectFormat format) { return format.execute(null, obj, spec); } } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyObject_GetIter extends CApiUnaryBuiltinNode { - @Specialization - static Object iter(Object object, - @Bind Node inliningTarget, - @Cached PyObjectGetIter getIter) { - return getIter.execute(null, inliningTarget, object); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Ignored) + static long GraalPyPrivate_Object_GetIter(long objectPtr) { + Object object = NativeToPythonInternalNode.executeUncached(objectPtr, false); + Object result = PyObjectGetIter.executeUncached(object); + return PythonToNativeInternalNode.executeNewRefUncached(result); } + /* + * Moving this to pure C is much faster (100x) for true native tp_hash, but hashing managed + * objects (e.g. Strings) regresses (also by ~100x). + */ @CApiBuiltin(ret = Py_hash_t, args = {PyObject}, call = Direct) abstract static class PyObject_Hash extends CApiUnaryBuiltinNode { @Specialization @@ -710,16 +691,6 @@ static long hash(Object object, } } - @CApiBuiltin(ret = Int, args = {PyObject}, call = Direct) - abstract static class PyCallable_Check extends CApiUnaryBuiltinNode { - @Specialization - static int doGeneric(Object object, - @Bind Node inliningTarget, - @Cached PyCallableCheckNode callableCheck) { - return intValue(callableCheck.execute(inliningTarget, object)); - } - } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) abstract static class PyObject_Dir extends CApiUnaryBuiltinNode { @Specialization 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 571a273492..840733bf5a 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 @@ -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,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; @@ -62,6 +64,7 @@ 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.object.PythonObject; import com.oracle.graal.python.builtins.objects.thread.PThread; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; @@ -71,7 +74,6 @@ 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.object.PFactory; import com.oracle.graal.python.util.OverflowException; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.ThreadLocalAction; @@ -79,67 +81,72 @@ 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.nodes.RootNode; public final class PythonCextPyStateBuiltins { - @CApiBuiltin(ret = Int, args = {}, acquireGil = false, call = Ignored) - abstract static class GraalPyPrivate_GILState_Check extends CApiNullaryBuiltinNode { + private static final TruffleLogger LOGGER = CApiContext.getLogger(PythonCextPyStateBuiltins.class); - @Specialization - Object check() { - return PythonContext.get(this).ownsGil() ? 1 : 0; - } + @CApiBuiltin(ret = Int, args = {}, acquireGil = false, call = Ignored) + static int GraalPyPrivate_GILState_Check() { + return PythonContext.get(null).ownsGil() ? 1 : 0; } @CApiBuiltin(ret = Int, args = {}, acquireGil = false, call = Ignored) - abstract static class GraalPyPrivate_GILState_Ensure extends CApiNullaryBuiltinNode { - - @Specialization - static Object save(@Cached GilNode gil) { - boolean acquired = gil.acquire(); - return acquired ? 1 : 0; - } + static int GraalPyPrivate_GILState_Ensure() { + boolean acquired = GilNode.getUncached().acquire(); + return acquired ? 1 : 0; } @CApiBuiltin(ret = Void, args = {}, acquireGil = false, call = Ignored) - abstract static class GraalPyPrivate_GILState_Release extends CApiNullaryBuiltinNode { - - @Specialization - static Object restore( - @Cached GilNode gil) { - gil.release(true); - return PNone.NO_VALUE; - } + static void GraalPyPrivate_GILState_Release() { + GilNode.getUncached().release(true); } - @CApiBuiltin(ret = PyThreadState, args = {Pointer}, call = Ignored) - abstract static class GraalPyPrivate_ThreadState_Get extends CApiUnaryBuiltinNode { - - @Specialization(limit = "1") - static Object get(Object tstateCurrentPtr, - @Bind Node inliningTarget, - @Bind PythonContext context, - @CachedLibrary("tstateCurrentPtr") InteropLibrary lib) { - PythonThreadState pythonThreadState = context.getThreadState(context.getLanguage(inliningTarget)); - if (!lib.isNull(tstateCurrentPtr)) { - pythonThreadState.setNativeThreadLocalVarPointer(tstateCurrentPtr); - } - return PThreadState.getOrCreateNativeThreadState(pythonThreadState); + /** + * Very unlikely fallback for threads that were already attached when another thread initialized + * the C API, but were blocked at that time and therefore could not process the thread-local + * action that eagerly initializes their native 'tstate_current' TLS slot. + */ + @CApiBuiltin(ret = PyThreadState, args = {Pointer}, acquireGil = false, call = Ignored) + static long GraalPyPrivate_ThreadState_Get(long tstateCurrentPtr) { + + PythonContext context = PythonContext.get(null); + PythonThreadState threadState = context.getThreadState(context.getLanguage()); + + /* + * The C caller may have observed 'tstate_current == NULL' before entering this upcall. + * While entering this builtin, the same thread may process a queued thread-local action + * from C API initialization and initialize its native thread state eagerly. So the + * fallback decision made in C can be stale by the time we get here. + */ + 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())); + long nativeThreadState = threadState.getNativePointer(); + assert nativeThreadState != NULLPTR; + return nativeThreadState; } + + LOGGER.fine(() -> "Lazy (fallback) initialization of native thread state for thread " + Thread.currentThread()); + assert threadState.getNativePointer() == PythonObject.UNINITIALIZED; + long nativeThreadState = PThreadState.getOrCreateNativeThreadState(threadState); + threadState.setNativeThreadLocalVarPointer(tstateCurrentPtr); + return nativeThreadState; } @CApiBuiltin(ret = Void, args = {}, call = Ignored) - abstract static class GraalPyPrivate_BeforeThreadDetach extends CApiNullaryBuiltinNode { - @Specialization - @TruffleBoundary - Object doIt() { - getContext().disposeThread(Thread.currentThread(), true); - return PNone.NO_VALUE; - } + static Object GraalPyPrivate_BeforeThreadDetach() { + /* + * This is the PyGILState_Release path for native-created threads. CPython clears its own + * auto-TSS key and deletes the PyThreadState here. Our Java thread state is stored in a + * Truffle ContextThreadLocal, which has no per-thread clear operation for a still-alive + * thread. If the same native thread later enters the context again, Truffle may return the + * same PythonThreadState object. Therefore this is a detach, not final thread shutdown: free + * the native PyThreadState, but leave the Java state usable for the next attach. + */ + PythonContext.get(null).disposeThread(Thread.currentThread(), true, false); + return PNone.NO_VALUE; } @CApiBuiltin(ret = PyObjectBorrowed, args = {}, call = Direct) @@ -151,12 +158,7 @@ static PDict get( @Bind Node inliningTarget, @Bind PythonContext context) { PythonThreadState threadState = context.getThreadState(context.getLanguage(inliningTarget)); - PDict threadStateDict = threadState.getDict(); - if (threadStateDict == null) { - threadStateDict = PFactory.createDict(context.getLanguage()); - threadState.setDict(threadStateDict); - } - return threadStateDict; + return PThreadState.getOrCreateThreadStateDict(context, threadState); } } @@ -215,10 +217,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; } } @@ -231,11 +233,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..9973ce5785 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,8 @@ 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 static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; @@ -57,6 +59,10 @@ 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.CExtNodes.EnsurePythonObjectNode; +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.PythonToNativeInternalNode; import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes; import com.oracle.graal.python.builtins.objects.common.HashingStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem; @@ -91,18 +97,23 @@ public final class PythonCextSetBuiltins { - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PySet_New extends CApiUnaryBuiltinNode { - @Specialization(guards = {"!isNone(iterable)", "!isNoValue(iterable)"}) - static Object newSet(Object iterable, - @Cached ConstructSetNode constructSetNode) { - return constructSetNode.execute(null, iterable); - } + private static final CApiTiming TIMING_PYSET_NEW = CApiTiming.create(false, "PySet_New"); - @Specialization - static Object newSet(@SuppressWarnings("unused") PNone iterable, - @Bind PythonLanguage language) { - return PFactory.createSet(language); + @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct, acquireGil = false) + static long PySet_New(long iterablePtr) { + CApiTiming.enter(); + try { + PSet set; + if (iterablePtr == NULLPTR) { + set = PFactory.createSet(PythonLanguage.get(null)); + } else { + Object iterable = NativeToPythonInternalNode.executeUncached(iterablePtr, false); + set = ConstructSetNode.getUncached().execute(null, iterable); + } + assert EnsurePythonObjectNode.doesNotNeedPromotion(set); + return PythonToNativeInternalNode.executeUncached(set, true); + } finally { + CApiTiming.exit(TIMING_PYSET_NEW); } } @@ -162,7 +173,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 +209,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); @@ -286,6 +297,10 @@ static int add(Object self, @SuppressWarnings("unused") Object o, } } + /* + * A pure-C implementation regressed a mixed managed/native set/frozenset workload by about + * 1.16x. + */ @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Direct) abstract static class PySet_Size extends CApiUnaryBuiltinNode { @Specialization @@ -296,7 +311,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..249648cc6a 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 @@ -44,7 +44,6 @@ 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.PyASCIIObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyByteArrayObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyCFunctionObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyCMethodObject; @@ -62,26 +61,24 @@ 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; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObjectBorrowed; -import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyUnicodeObject; 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_ssize_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.UNSIGNED_INT; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void; 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.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.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; @@ -91,39 +88,33 @@ 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.structs.CStructAccess; +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.common.HashingStorageNodes.HashingStorageLen; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; import com.oracle.graal.python.builtins.objects.frame.PFrame; 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; @@ -134,105 +125,21 @@ 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.profiles.InlinedConditionProfile; -import com.oracle.truffle.api.strings.TruffleString; 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 { - - @Specialization - static Object get(PSequence object) { - assert !(object.getSequenceStorage() instanceof NativeByteSequenceStorage); - return PySequenceArrayWrapper.ensureNativeSequence(object); - } - } - - @CApiBuiltin(ret = Py_ssize_t, args = {PyASCIIObject}, call = Ignored) - abstract static class GraalPyPrivate_Get_PyASCIIObject_length extends CApiUnaryBuiltinNode { - - @Specialization - static long get(Object object, - @Cached StringLenNode stringLenNode) { - return stringLenNode.execute(object); - } - } - - @CApiBuiltin(ret = UNSIGNED_INT, args = {PyASCIIObject}, call = Ignored) - abstract static class GraalPyPrivate_Get_PyASCIIObject_state_ascii extends CApiUnaryBuiltinNode { - - @Specialization - int get(PString object, - @Bind Node inliningTarget, - @Cached InlinedConditionProfile storageProfile, - @Cached HiddenAttr.ReadNode readAttrNode, - @Cached TruffleString.GetCodeRangeNode getCodeRangeNode) { - // important: avoid materialization of native sequences - NativeStringData nativeData = object.getNativeStringData(inliningTarget, readAttrNode); - if (storageProfile.profile(inliningTarget, nativeData != null)) { - return nativeData.isAscii() ? 1 : 0; - } - - TruffleString string = object.getMaterialized(); - return PInt.intValue(getCodeRangeNode.execute(string, TS_ENCODING) == TruffleString.CodeRange.ASCII); - } + @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); } - @CApiBuiltin(ret = UNSIGNED_INT, args = {PyASCIIObject}, call = Ignored) - abstract static class GraalPyPrivate_Get_PyASCIIObject_state_compact extends CApiUnaryBuiltinNode { - - @Specialization - static int get(@SuppressWarnings("unused") Object object) { - return 0; - } - } - - @CApiBuiltin(ret = UNSIGNED_INT, args = {PyASCIIObject}, call = Ignored) - abstract static class GraalPyPrivate_Get_PyASCIIObject_state_interned extends CApiUnaryBuiltinNode { - - @Specialization - static int get(PString object, - @Bind Node inliningTarget, - @Cached StringNodes.IsInternedStringNode isInternedStringNode) { - return isInternedStringNode.execute(inliningTarget, object) ? 1 : 0; - } - } - - @CApiBuiltin(ret = UNSIGNED_INT, args = {PyASCIIObject}, call = Ignored) - abstract static class GraalPyPrivate_Get_PyASCIIObject_state_kind extends CApiUnaryBuiltinNode { - - @Specialization - static int get(PString object, - @Bind Node inliningTarget, - @Cached InlinedConditionProfile storageProfile, - @Cached HiddenAttr.ReadNode readAttrNode, - @Cached TruffleString.GetCodeRangeNode getCodeRangeNode) { - // important: avoid materialization of native sequences - NativeStringData nativeData = object.getNativeStringData(inliningTarget, readAttrNode); - if (storageProfile.profile(inliningTarget, nativeData != null)) { - return nativeData.getCharSize(); - } - TruffleString string = object.getMaterialized(); - TruffleString.CodeRange range = getCodeRangeNode.execute(string, TS_ENCODING); - if (range.isSubsetOf(TruffleString.CodeRange.LATIN_1)) { - return 1; - } else if (range.isSubsetOf(TruffleString.CodeRange.BMP)) { - return 2; - } else { - return 4; - } - } - } - - @CApiBuiltin(ret = UNSIGNED_INT, args = {PyASCIIObject}, call = Ignored) - abstract static class GraalPyPrivate_Get_PyASCIIObject_state_ready extends CApiUnaryBuiltinNode { - - @Specialization - static int get(@SuppressWarnings("unused") Object object) { - return 1; - } + @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 = PyTypeObjectBorrowed, args = {PyCMethodObject}, call = Ignored) @@ -246,9 +153,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 +165,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 +177,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 +190,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 +201,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 +240,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 +249,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 +294,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 +315,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 +323,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 +339,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 +356,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 +417,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 +425,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 +433,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 +441,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 +458,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(); } @@ -626,11 +518,12 @@ abstract static class GetSliceField extends Node { static Object get(Node inliningTarget, PSlice object, HiddenAttr key, Object value, @Cached HiddenAttr.ReadNode read, @Cached HiddenAttr.WriteNode write, - @Cached PythonCextBuiltins.PromoteBorrowedValue promote) { + @Bind PythonContext context, + @Cached CExtNodes.EnsurePythonObjectNode ensureNode) { Object promotedValue = read.execute(inliningTarget, object, key, null); if (promotedValue == null) { - promotedValue = promote.execute(inliningTarget, value); - if (promotedValue == null) { + promotedValue = ensureNode.execute(context, value, false); + if (promotedValue == value) { return value; } write.execute(inliningTarget, object, key, promotedValue); @@ -672,55 +565,6 @@ static Object doStop(PSlice object, } } - @CApiBuiltin(ret = Pointer, args = {PyUnicodeObject}, call = Ignored) - abstract static class GraalPyPrivate_Get_PyUnicodeObject_data extends CApiUnaryBuiltinNode { - - @Specialization - static Object 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) { - NativeStringData nativeData = object.getNativeStringData(inliningTarget, readAttrNode); - if (nativeData != null) { - // in this case, we can just return the pointer - return nativeData.getPtr(); - } - TruffleString string = object.getMaterialized(); - TruffleString.CodeRange range = getCodeRangeNode.execute(string, TS_ENCODING); - TruffleString.Encoding encoding; - int charSize; - boolean isAscii = false; - if (range == TruffleString.CodeRange.ASCII) { - isAscii = true; - charSize = 1; - encoding = TruffleString.Encoding.US_ASCII; - } else if (range.isSubsetOf(TruffleString.CodeRange.LATIN_1)) { - charSize = 1; - encoding = TruffleString.Encoding.ISO_8859_1; - } else if (range.isSubsetOf(TruffleString.CodeRange.BMP)) { - charSize = 2; - encoding = TruffleString.Encoding.UTF_16; - } else { - charSize = 4; - encoding = TruffleString.Encoding.UTF_32; - } - string = switchEncodingNode.execute(string, encoding); - int byteLength = string.byteLength(encoding); - Object ptr = allocateNode.alloc(byteLength + /* null terminator */ charSize); - writeTruffleStringNode.write(ptr, string, encoding); - /* - * Set native data, so we can just return the pointer the next time. - */ - NativeStringData data = NativeStringData.create(charSize, isAscii, ptr, byteLength); - object.setNativeStringData(inliningTarget, writeAttrNode, data); - return ptr; - } - } - @CApiBuiltin(ret = Py_ssize_t, args = {PyVarObject}, call = Ignored) abstract static class GraalPyPrivate_Get_PyVarObject_ob_size extends CApiUnaryBuiltinNode { 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..a6e08c511b 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,13 @@ 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); + names.add(FromCharPointerNode.executeUncached(name)); + docs.add(doc == NULLPTR ? null : FromCharPointerNode.executeUncached(doc)); pos++; } @@ -126,7 +122,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 +132,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..8f40f49c60 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,25 +43,27 @@ 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.CExtNodes.EnsurePythonObjectNode; +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; import com.oracle.graal.python.lib.PyObjectSetAttr; +import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.truffle.api.CompilerAsserts; @@ -80,17 +82,18 @@ abstract static class PySys_GetObject extends CApiUnaryBuiltinNode { @Specialization Object getObject(TruffleString name, @Bind Node inliningTarget, - @Cached PromoteBorrowedValue promoteNode, + @Bind PythonContext context, + @Cached EnsurePythonObjectNode ensureNode, @Cached PyObjectLookupAttr lookupNode, @Cached PyObjectSetAttr setAttrNode) { try { 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) { + Object promotedValue = ensureNode.execute(context, value, false); + if (promotedValue != value) { setAttrNode.execute(inliningTarget, sys, name, promotedValue); return promotedValue; } @@ -98,7 +101,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 +118,32 @@ 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); + 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; + } + + @CApiBuiltin(ret = Int, args = {ConstCharPtr, PyObject}, call = Ignored, acquireGil = false) + @TruffleBoundary + public static int GraalPyPrivate_Sys_Audit(long eventPtr, long argsPtr) { + TruffleString event = FromCharPointerNode.executeUncached(eventPtr); + Object args = NativeToPythonInternalNode.executeUncached(argsPtr, false); + for (Object hook : PythonContext.get(null).getAuditHooks()) { + CallNode.executeUncached(hook, event, args); } + 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..e4409a8319 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 @@ -46,8 +46,10 @@ 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.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.runtime.PythonContext.NATIVE_NULL; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; @@ -55,10 +57,10 @@ 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.PythonAbstractNativeObject; -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.PythonToNativeInternalNode; 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; @@ -72,8 +74,8 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.TupleNodes.GetNativeTupleStorage; +import com.oracle.graal.python.runtime.PythonContext; 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.SequenceStorage; import com.oracle.truffle.api.dsl.Bind; @@ -86,27 +88,9 @@ public final class PythonCextTupleBuiltins { - @CApiBuiltin(ret = PyObjectTransfer, args = {Py_ssize_t}, call = Direct) - abstract static class PyTuple_New extends CApiUnaryBuiltinNode { - - @Specialization - static PTuple doGeneric(long longSize, - @Bind Node inliningTarget, - @Bind PythonLanguage language, - @Cached PRaiseNode raiseNode, - @Cached CStructAccess.AllocateNode alloc) { - int size = (int) longSize; - if (longSize != size) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.MemoryError); - } - /* - * 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); - NativeObjectSequenceStorage storage = NativeObjectSequenceStorage.create(mem, size, size, true); - return PFactory.createTuple(language, storage); - } + @CApiBuiltin(ret = PyObjectRawPointer, call = Ignored) + static long GraalPyPrivate_Tuple_Empty() { + return PythonToNativeInternalNode.executeNewRefUncached(PFactory.createEmptyTuple(PythonLanguage.get(null))); } @CApiBuiltin(ret = PyObjectBorrowed, args = {PyObject, Py_ssize_t}, call = Ignored) @@ -117,7 +101,8 @@ public abstract static class GraalPyPrivate_Tuple_GetItem extends CApiBinaryBuil @Specialization static Object doPTuple(PTuple tuple, long key, @Bind Node inliningTarget, - @Exclusive @Cached PromoteBorrowedValue promoteNode, + @Bind PythonContext context, + @Exclusive @Cached EnsurePythonObjectNode ensureNode, @Exclusive @Cached ListGeneralizationNode generalizationNode, @Exclusive @Cached SetItemScalarNode setItemNode, @Exclusive @Cached GetItemScalarNode getItemNode, @@ -126,8 +111,8 @@ static Object doPTuple(PTuple tuple, long key, int index = checkIndex(inliningTarget, key, sequenceStorage, raiseNode); Object result = getItemNode.execute(inliningTarget, sequenceStorage, index); assert result != null : "tuple must not contain Java null"; - Object promotedValue = promoteNode.execute(inliningTarget, result); - if (promotedValue != null) { + Object promotedValue = ensureNode.execute(context, result, false); + if (promotedValue != result) { sequenceStorage = generalizationNode.execute(inliningTarget, sequenceStorage, promotedValue); tuple.setSequenceStorage(sequenceStorage); setItemNode.execute(inliningTarget, sequenceStorage, index, promotedValue); @@ -139,22 +124,23 @@ static Object doPTuple(PTuple tuple, long key, @Specialization static Object doNative(PythonAbstractNativeObject tuple, long key, @Bind Node inliningTarget, + @Bind PythonContext context, @Exclusive @Cached GetNativeTupleStorage asNativeStorage, - @Exclusive @Cached PromoteBorrowedValue promoteNode, + @Exclusive @Cached EnsurePythonObjectNode ensureNode, @Exclusive @Cached SetItemScalarNode setItemNode, @Exclusive @Cached GetItemScalarNode getItemNode, @Exclusive @Cached PRaiseNode raiseNode) { SequenceStorage sequenceStorage = asNativeStorage.execute(tuple); int index = checkIndex(inliningTarget, key, sequenceStorage, raiseNode); Object result = getItemNode.execute(inliningTarget, sequenceStorage, index); - Object promotedValue = promoteNode.execute(inliningTarget, result); - if (promotedValue != null) { + if (result == null) { + return NATIVE_NULL; + } + Object promotedValue = ensureNode.execute(context, result, false); + if (promotedValue != result) { setItemNode.execute(inliningTarget, sequenceStorage, index, promotedValue); return promotedValue; } - if (result == null) { - return getNativeNull(inliningTarget); - } return result; } @@ -172,10 +158,13 @@ private static int checkIndex(Node inliningTarget, long key, SequenceStorage seq } } + /* + * The best attempt at moving this to pure C regressed by about 1.09x by median time. + */ @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 +204,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..5554ba51f6 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.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.runtime.nativeaccess.NativeMemory.NULLPTR; 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; @@ -75,13 +76,20 @@ import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext; import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodes.ReadMemberNode; import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodes.WriteMemberNode; +import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode; import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes; 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.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,33 +106,32 @@ 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.nodes.ErrorMessages; import com.oracle.graal.python.nodes.HiddenAttr; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.PRootNode; 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.nativeaccess.NativeFunctionPointer; 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; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; @@ -137,9 +144,10 @@ abstract static class _PyType_Lookup extends CApiBinaryBuiltinNode { @Specialization Object doGeneric(Object type, Object name, @Bind Node inliningTarget, + @Bind PythonContext context, @Cached CastToTruffleStringNode castToTruffleStringNode, @Cached TypeNodes.GetMroStorageNode getMroStorageNode, - @Cached PythonCextBuiltins.PromoteBorrowedValue promoteBorrowedValue, + @Cached EnsurePythonObjectNode ensureNode, @Cached CStructAccess.ReadObjectNode getNativeDict, @Cached GetDictIfExistsNode getDictIfExistsNode, @Cached DynamicObject.GetNode getNode, @@ -170,8 +178,8 @@ Object doGeneric(Object type, Object name, value = getItem.execute(null, inliningTarget, dict, key); } if (value != null && value != PNone.NO_VALUE) { - Object promoted = promoteBorrowedValue.execute(inliningTarget, value); - if (promoted != null) { + Object promoted = ensureNode.execute(context, value, false); + if (promoted != value) { if (dict == null) { putNode.execute((PythonManagedClass) cls, key, promoted); } else { @@ -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,192 @@ 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); - } - writeAttrNode.execute(function, T___NAME__, name); - writeAttrNode.execute(function, T___DOC__, doc); - return function; + /** + * 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 (func != null) { + WriteAttributeToPythonObjectNode.executeUncached(func, T___NAME__, name); + CFunctionDocUtils.writeDocAndTextSignature(func, name, doc); } - - @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; + 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); + } + assert func != null; + return PFactory.createBuiltinClassmethodFromCallableObj(language, func); + } else if (CExtContext.isMethStatic(flags)) { + return PFactory.createStaticmethodFromCallableObj(language, func); } + 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.executeUncached(nameRaw); + Object doc = CharPtrToPythonNode.executeUncached(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) NativeToPythonInternalNode.executeUncached(tpDictPtr, false); + TruffleString memberName = (TruffleString) CharPtrToPythonNode.executeUncached(memberNamePtr); + Object memberDoc = memberDocPtr == NULLPTR ? PNone.NO_VALUE : CharPtrToPythonNode.executeUncached(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) NativeToPythonInternalNode.executeUncached(dictPtr, false); + TruffleString name = (TruffleString) CharPtrToPythonNode.executeUncached(namePtr); + Object doc = docPtr == NULLPTR ? PNone.NO_VALUE : CharPtrToPythonNode.executeUncached(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) { + PRootNode getterRoot = getterRootNode(name, language); + NativeFunctionPointer getterFun = CExtCommonNodes.bindFunctionPointer(getter, PExternalFunctionWrapper.GETTER); + get = PFactory.createBuiltinFunction(language, name, cls, EMPTY_OBJECT_ARRAY, ExternalFunctionNodes.createKwDefaults(getterFun, closure), 0, getterRoot); } - @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) { + PRootNode setterRoot = setterRootNode(name, language); + NativeFunctionPointer setterFun = CExtCommonNodes.bindFunctionPointer(setter, PExternalFunctionWrapper.SETTER); + set = PFactory.createBuiltinFunction(language, name, cls, EMPTY_OBJECT_ARRAY, ExternalFunctionNodes.createKwDefaults(setterFun, closure), 0, setterRoot); } - @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 PRootNode getterRootNode(TruffleString name, PythonLanguage lang) { + Function rootNodeFunction = l -> WrapperDescriptorRootNodesGen.create(l, name, PExternalFunctionWrapper.GETTER); + return lang.createCachedExternalFunWrapperRootNode(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 PRootNode setterRootNode(TruffleString name, PythonLanguage lang) { + Function rootNodeFunction = l -> WrapperDescriptorRootNodesGen.create(l, name, PExternalFunctionWrapper.SETTER); + return lang.createCachedExternalFunWrapperRootNode(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 ea8e21333d..d3ff63eabb 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 @@ -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 @@ -68,8 +68,13 @@ 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.capi.transitions.CApiTransitions.GRAALPY_UNICODE_INTERN_STATE_INTERNED; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.GRAALPY_UNICODE_INTERN_STATE_NOT_INTERNED; +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.lib.PyUnicodeFSDecoderNode.SURROGATE_ESCAPE_FROM_UTF8_TRANSCODING_ERROR_HANDLER; 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,9 +84,10 @@ 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.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 static com.oracle.truffle.api.strings.TruffleString.Encoding.ISO_8859_1; -import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_16; import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_16LE; import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_32LE; import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_8; @@ -100,7 +106,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; @@ -108,18 +113,25 @@ 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.EnsurePythonObjectNode; +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; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.AllocateNativeObjectStubNode; +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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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; import com.oracle.graal.python.builtins.objects.cext.structs.CStructs; +import com.oracle.graal.python.builtins.objects.exception.UnicodeDecodeErrorBuiltins.MakeDecodeExceptionNode; 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.str.NativeStringData; import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.str.StringBuiltins; import com.oracle.graal.python.builtins.objects.str.StringBuiltins.EncodeNode; @@ -133,7 +145,6 @@ import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PySliceNew; import com.oracle.graal.python.lib.PyTupleGetItem; -import com.oracle.graal.python.lib.PyUnicodeCheckExactNode; import com.oracle.graal.python.lib.PyUnicodeFSDecoderNode; import com.oracle.graal.python.lib.PyUnicodeFromEncodedObject; import com.oracle.graal.python.lib.RichCmpOp; @@ -152,9 +163,11 @@ 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.nativeaccess.NativeMemory; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.ConcurrentWeakSet; 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; @@ -166,15 +179,15 @@ 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; +import com.oracle.truffle.api.strings.TruffleString.FromNativePointerWithCompactionUTF32Node; +import com.oracle.truffle.api.strings.TruffleString.IsValidNode; import com.oracle.truffle.api.strings.TruffleString.SwitchEncodingNode; import com.oracle.truffle.api.strings.TruffleStringBuilder; import com.oracle.truffle.api.strings.TruffleStringBuilderUTF32; @@ -309,33 +322,52 @@ static Object doGeneric(Object obj, Object encodingObj, Object errorsObj, } } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Ignored) - abstract static class GraalPyPrivate_Unicode_LookupAndIntern extends CApiUnaryBuiltinNode { + private static final CApiTiming TIMING_GRAALPYPRIVATE_UNICODE_LOOKUPANDINTERN = CApiTiming.create(false, "GraalPyPrivate_Unicode_LookupAndIntern"); - @Specialization - static Object withPString(PString str, - @Bind Node inliningTarget, - @Cached PyUnicodeCheckExactNode unicodeCheckExactNode) { - if (!unicodeCheckExactNode.execute(inliningTarget, str)) { - return getNativeNull(inliningTarget); + @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Ignored) + static long GraalPyPrivate_Unicode_LookupAndIntern(long objPtr) { + CApiTiming.enter(); + try { + Object obj = NativeToPythonInternalNode.executeUncached(objPtr, false); + if (obj instanceof PString str) { + if (!PGuards.isBuiltinPString(str)) { + return NULLPTR; + } + str.intern(); + if (str.isNative()) { + long ptr = HandlePointerConverter.pointerToStub(str.getNativePointer()); + CApiTransitions.setGraalPyUnicodeObjectInterned(ptr, GRAALPY_UNICODE_INTERN_STATE_INTERNED); + } + /* + * TODO this is not integrated with str.intern, pointer comparisons of two + * str.intern'ed string may still yield failse + */ + ConcurrentWeakSet interningCache = PythonContext.get(null).getCApiContext().getPstringInterningCache(); + PString interned = interningCache.intern(str, s -> s); + assert EnsurePythonObjectNode.doesNotNeedPromotion(interned); + return PythonToNativeInternalNode.executeUncached(interned, true); + } else { + /* + * If it's a subclass, we don't really know what putting it in the interned dict might + * do. + */ + return NULLPTR; } + } finally { + CApiTiming.exit(TIMING_GRAALPYPRIVATE_UNICODE_LOOKUPANDINTERN); + } + } - str.intern(); - /* - * TODO this is not integrated with str.intern, pointer comparisons of two str.intern'ed - * string may still yield failse - */ - ConcurrentWeakSet interningCache = PythonContext.get(inliningTarget).getCApiContext().getPstringInterningCache(); - return interningCache.intern(str, s -> s); + @CApiBuiltin(ret = Int, args = {PyObject}, call = Ignored) + abstract static class GraalPyPrivate_Unicode_CheckInterned extends CApiUnaryBuiltinNode { + @Specialization + static int withPString(PString str) { + return PythonUtils.isInterned(str.getValueUncached()) ? 1 : 0; } @Fallback - 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(); + static int nil(@SuppressWarnings("unused") Object obj) { + return 0; } } @@ -458,27 +490,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, @@ -563,13 +595,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); } } @@ -603,7 +670,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, @@ -617,7 +684,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, @@ -632,7 +699,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) { @@ -691,7 +758,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; } } @@ -700,7 +767,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, @@ -788,53 +855,48 @@ 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, - @Bind Node inliningTarget, - @Bind PythonLanguage language, - @Cached HiddenAttr.WriteNode writeNode, - @Cached PRaiseNode raiseNode) { - long size = elements * charSize; - if (!PInt.isIntRange(size)) { - throw raiseNode.raise(inliningTarget, MemoryError); - } - PString s = PFactory.createString(language, null); - NativeStringData data = NativeStringData.create(charSize, isAscii != 0, ptr, (int) size); - s.setNativeStringData(inliningTarget, writeNode, data); - return s; - } + @CApiBuiltin(ret = PyObject, args = {Py_ssize_t, Int, Int}, call = Ignored) + static long GraalPyPrivate_Unicode_New(long nChars, int charSize, int isAscii) { + if (nChars < 0 || nChars > Long.MAX_VALUE / charSize) { + throw PRaiseNode.raiseStatic(null, MemoryError); + } + long size = nChars * charSize; + if (!PInt.isIntRange(size)) { + throw PRaiseNode.raiseStatic(null, MemoryError); + } + // the extra 'charSize' accounts for the NUL char + long extraSize = size + charSize; + PString s = PFactory.createString(PythonLanguage.get(null), null); + long initialRefCount = FirstToNativeNode.getInitialRefcnt(true, false); + long taggedPointer = AllocateNativeObjectStubNode.executeUncached(s, PythonBuiltinClassType.PString, CStructs.GraalPyUnicodeObject, initialRefCount, false, extraSize); + s.setNativePointer(taggedPointer); + long realPointer = HandlePointerConverter.pointerToStub(taggedPointer); + CApiTransitions.initializeGraalPyUnicodeObject(realPointer, nChars, size, charSize, isAscii != 0, GRAALPY_UNICODE_INTERN_STATE_NOT_INTERNED); + return taggedPointer; } @CApiBuiltin(ret = PyObjectTransfer, args = {Pointer, Py_ssize_t, Int}, call = Ignored) abstract static class GraalPyPrivate_Unicode_FromUCS extends CApiTernaryBuiltinNode { - private static Encoding encodingFromKind(Node inliningTarget, int kind, PRaiseNode raiseNode) throws PException { + private static TruffleString.CompactionLevel compactionLevelFromKind(Node inliningTarget, int kind, PRaiseNode raiseNode) throws PException { return switch (kind) { - case 1 -> ISO_8859_1; - case 2 -> UTF_16; - case 4 -> TS_ENCODING; + case 1 -> TruffleString.CompactionLevel.S1; + case 2 -> TruffleString.CompactionLevel.S2; + case 4 -> TruffleString.CompactionLevel.S4; default -> throw raiseNode.raiseBadInternalCall(inliningTarget); }; } @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, + @Cached FromNativePointerWithCompactionUTF32Node fromNativePointerNode, @Cached PRaiseNode raiseNode) { try { int iByteLength = PInt.intValueExact(byteLength); - Encoding srcEncoding = encodingFromKind(inliningTarget, kind, raiseNode); - /* - * TODO(fa): TruffleString does currently not support creating strings from UCS1 and - * UCS2 bytes (GR-44312). Remind: UCS1 and UCS2 are actually compacted UTF-32 bytes. - * For now, we use ISO-8859-1 and UTF-16 but that's not entirely correct. - */ - TruffleString ts = fromNativePointerNode.execute(ptr, 0, iByteLength, srcEncoding, true); - return PFactory.createString(PythonLanguage.get(inliningTarget), switchEncodingNode.execute(ts, TS_ENCODING)); + TruffleString.CompactionLevel compactionLevel = compactionLevelFromKind(inliningTarget, kind, raiseNode); + TruffleString ts = fromNativePointerNode.execute(ptr, 0, iByteLength, compactionLevel, true); + return PFactory.createString(PythonLanguage.get(inliningTarget), ts); } catch (OverflowException e) { throw raiseNode.raise(inliningTarget, MemoryError); } @@ -851,9 +913,37 @@ static Object fsDecoder(Object arg, } } + @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtr, Py_ssize_t}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_Unicode_DecodeFSDefaultAndSize(long s, long lsize) { + // TODO: this implementation does not honor Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors + try { + int size = PInt.intValueExact(lsize); + TruffleString candidate = TruffleString.fromNativePointerUncached(s, 0, size, UTF_8, true); + TruffleString str; + if (candidate.isValidUncached(UTF_8)) { + str = candidate.switchEncodingUncached(TS_ENCODING); + } else { + str = candidate.switchEncodingUncached(TS_ENCODING, SURROGATE_ESCAPE_FROM_UTF8_TRANSCODING_ERROR_HANDLER); + } + // implicitly promotes TruffleString to PString + return PythonToNativeInternalNode.executeNewRefUncached(str); + } catch (OverflowException e) { + throw PRaiseNode.raiseStatic(null, MemoryError); + } + } + @CApiBuiltin(ret = PyObjectTransfer, args = {Pointer, Py_ssize_t, Int}, call = Ignored) abstract static class GraalPyPrivate_Unicode_FromUTF extends CApiTernaryBuiltinNode { + private static TruffleString encodingFromKind(int kind) { + return switch (kind) { + case 1 -> T_UTF8; + case 2 -> T_UTF_16_LE; + case 4 -> T_UTF_32_LE; + default -> throw CompilerDirectives.shouldNotReachHere(); + }; + } + private static Encoding encodingFromKind(Node inliningTarget, int kind, PRaiseNode raiseNode) throws PException { return switch (kind) { case 1 -> UTF_8; @@ -864,16 +954,23 @@ 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 IsValidNode isValidNode, + @Cached MakeDecodeExceptionNode makeDecodingExceptionNode, @Cached SwitchEncodingNode switchEncodingNode, @Cached PRaiseNode raiseNode) { try { int iByteLength = PInt.intValueExact(byteLength); Encoding srcEncoding = encodingFromKind(inliningTarget, kind, raiseNode); TruffleString ts = fromNativePointerNode.execute(ptr, 0, iByteLength, srcEncoding, true); - return PFactory.createString(PythonLanguage.get(inliningTarget), switchEncodingNode.execute(ts, TS_ENCODING)); + if (isValidNode.execute(ts, srcEncoding)) { + return PFactory.createString(PythonLanguage.get(inliningTarget), switchEncodingNode.execute(ts, TS_ENCODING)); + } else { + throw raiseNode.raiseExceptionObject(inliningTarget, makeDecodingExceptionNode.execute(null, inliningTarget, null, encodingFromKind(kind), ts, 0, iByteLength, + ErrorMessages.ILLEGAL_MULTIBYTE_SEQUENCE)); + } } catch (OverflowException e) { throw raiseNode.raise(inliningTarget, MemoryError); } @@ -915,19 +1012,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); } } } @@ -936,14 +1030,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; @@ -955,8 +1048,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); } } } @@ -965,14 +1056,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; @@ -984,8 +1074,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); } } } @@ -1028,7 +1116,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) { @@ -1096,12 +1184,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) { @@ -1110,15 +1196,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); } @@ -1129,17 +1215,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; } } @@ -1148,12 +1231,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) { @@ -1163,8 +1244,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); } @@ -1191,6 +1272,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; @@ -1201,16 +1283,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 @@ -1233,16 +1316,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 @@ -1261,16 +1334,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..138bae8354 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,38 @@ 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; +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.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(long pyObject) { + CApiTransitions.removeNativeWeakRef(PythonContext.get(null), pyObject); } - @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 +83,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 = NativeToPythonInternalNode.executeUncached(callbackPtr, false); } + Object object = NativeToPythonInternalNode.executeUncached(objectPtr, false); + Object proxy = PyObjectCallMethodObjArgs.executeUncached(weakrefModule, T_PROXY_TYPE, object, callback); + return PythonToNativeInternalNode.executeNewRefUncached(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 = NativeToPythonInternalNode.executeUncached(referencePtr, false); + 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 = NativeToPythonInternalNode.executeUncached(referencePtr, false); + 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 742695466d..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 @@ -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,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 54e5b17c7e..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 @@ -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,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 34a5d0f42a..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 @@ -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,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 1558fde04e..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 @@ -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,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 2f48bc253c..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 @@ -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,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 47c88f6a6f..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 @@ -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,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 c7fbfab82e..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 @@ -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,77 +40,60 @@ */ 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.nio.charset.Charset; +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); - Charset charset = CharsetMapping.getCharsetNormalized(normalizedEncoding); + CharsetMapping.CharsetWrapper charset = CharsetMapping.getCharsetNormalized(normalizedEncoding); if (charset != null) { if (cidx != -1) { - codecs[cidx] = new MultibyteCodec(tsName, charset, ct); + codecs[cidx] = new MultibyteCodec(tsName, charset.charset(), ct); } if (midx != -1) { - DBCSMap h = maps[midx] = new DBCSMap(name, tsName, charset, mt); - codec.setAttribute(toTruffleStringUncached(h.charsetMapName), - PFactory.createCapsuleJavaName(language, h, PyMultibyteCodec_CAPSULE_NAME)); + maps[midx] = new DBCSMap(name, tsName, charset.charset(), mt); } } } @@ -120,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/codecs/CharmapNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/CharmapNodes.java index bd6962ace1..584f6c5fb3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/CharmapNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/CharmapNodes.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 @@ -369,15 +369,14 @@ static TruffleString decodeLatin1(VirtualFrame frame, Object data, @SuppressWarn @Shared @Cached("createFor($node)") InteropCallData callData, @CachedLibrary("data") PythonBufferAcquireLibrary bufferAcquireLib, @CachedLibrary(limit = "3") @Shared PythonBufferAccessLibrary bufferLib, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { // equivalent of PyUnicode_DecodeLatin1 Object dataBuffer = bufferAcquireLib.acquireReadonly(data, frame, context, context.getLanguage(inliningTarget), callData); try { int len = bufferLib.getBufferLength(dataBuffer); byte[] src = bufferLib.getInternalOrCopiedByteArray(dataBuffer); - TruffleString latin1 = fromByteArrayNode.execute(src, 0, len, TruffleString.Encoding.ISO_8859_1, true); - return switchEncodingNode.execute(latin1, TS_ENCODING); + return fromByteArrayNode.execute(src, 0, len, TruffleString.CompactionLevel.S1, true); } finally { bufferLib.release(dataBuffer, frame, callData); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/CodecsRegistry.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/CodecsRegistry.java index c51e1e17f0..a79e32115a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/CodecsRegistry.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/CodecsRegistry.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 @@ -158,6 +158,13 @@ static void ensure(VirtualFrame frame, Node inliningTarget, PythonContext contex } } + @TruffleBoundary + public static void initialize(PythonContext context) { + if (!context.isCodecsInitialized()) { + doInitialize(context); + } + } + @TruffleBoundary private static void doInitialize(PythonContext context) { registerDefaultHandler(context, T_STRICT, StrictErrorHandlerNodeFactory.getInstance()); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java index 62c6d0a47a..c7103d9d74 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.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 @@ -364,8 +364,7 @@ static Object doEncode(PBaseException exception, @Cached PyUnicodeEncodeOrTranslateErrorGetStartNode getStartNode, @Cached PyUnicodeEncodeOrTranslateErrorGetEndNode getEndNode, @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { TruffleString src = getObjectNode.execute(inliningTarget, exception); int start = getStartNode.execute(inliningTarget, exception); int end = getEndNode.execute(inliningTarget, exception); @@ -378,8 +377,8 @@ static Object doEncode(PBaseException exception, for (int i = start; i < end; ++i) { pos = appendXmlCharRefReplacement(replacement, pos, codePointAtIndexNode.execute(src, i)); } - TruffleString resultAscii = fromByteArrayNode.execute(replacement, Encoding.US_ASCII, false); - return PFactory.createTuple(language, new Object[]{switchEncodingNode.execute(resultAscii, TS_ENCODING), end}); + TruffleString resultAscii = fromByteArrayNode.execute(replacement, 0, replacement.length, TruffleString.CompactionLevel.S1, false); + return PFactory.createTuple(language, new Object[]{resultAscii, end}); } @Specialization(guards = "!isEncode(inliningTarget, o, pyObjectTypeCheck)", limit = "1") @@ -405,8 +404,7 @@ static Object doDecodeException(VirtualFrame frame, PBaseException exception, @Cached PyUnicodeDecodeErrorGetEndNode getEndNode, @CachedLibrary(limit = "3") PythonBufferAcquireLibrary acquireLib, @CachedLibrary(limit = "3") PythonBufferAccessLibrary accessLib, - @Cached @Shared TruffleString.FromByteArrayNode fromByteArrayNode, - @Cached @Shared TruffleString.SwitchEncodingNode switchEncodingNode) { + @Cached @Shared TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { int start = getStartNode.execute(inliningTarget, exception); int end = getEndNode.execute(inliningTarget, exception); Object object = getObjectNode.execute(inliningTarget, exception); @@ -424,8 +422,8 @@ static Object doDecodeException(VirtualFrame frame, PBaseException exception, } finally { accessLib.release(srcBuf, frame, callData); } - TruffleString resultAscii = fromByteArrayNode.execute(replacement, Encoding.US_ASCII, false); - return PFactory.createTuple(language, new Object[]{switchEncodingNode.execute(resultAscii, TS_ENCODING), end}); + TruffleString resultAscii = fromByteArrayNode.execute(replacement, 0, replacement.length, TruffleString.CompactionLevel.S1, false); + return PFactory.createTuple(language, new Object[]{resultAscii, end}); } @Specialization(guards = "isEncodeOrTranslate(inliningTarget, exception, pyObjectTypeCheck)", limit = "1") @@ -437,8 +435,7 @@ static Object doEncodeOrTranslateException(PBaseException exception, @Cached PyUnicodeEncodeOrTranslateErrorGetStartNode getStartNode, @Cached PyUnicodeEncodeOrTranslateErrorGetEndNode getEndNode, @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode, - @Cached @Shared TruffleString.FromByteArrayNode fromByteArrayNode, - @Cached @Shared TruffleString.SwitchEncodingNode switchEncodingNode) { + @Cached @Shared TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { int start = getStartNode.execute(inliningTarget, exception); int end = getEndNode.execute(inliningTarget, exception); TruffleString src = getObjectNode.execute(inliningTarget, exception); @@ -462,8 +459,8 @@ static Object doEncodeOrTranslateException(PBaseException exception, int cp = codePointAtIndexNode.execute(src, i); pos = BytesUtils.unicodeNonAsciiEscape(cp, pos, replacement, true); } - TruffleString resultAscii = fromByteArrayNode.execute(replacement, Encoding.US_ASCII, false); - return PFactory.createTuple(language, new Object[]{switchEncodingNode.execute(resultAscii, TS_ENCODING), end}); + TruffleString resultAscii = fromByteArrayNode.execute(replacement, 0, replacement.length, TruffleString.CompactionLevel.S1, false); + return PFactory.createTuple(language, new Object[]{resultAscii, end}); } @Specialization(guards = "isNeither(inliningTarget, o, pyObjectTypeCheck)", limit = "1") @@ -487,8 +484,7 @@ static Object doEncode(PBaseException exception, @Cached PyUnicodeEncodeOrTranslateErrorGetStartNode getStartNode, @Cached PyUnicodeEncodeOrTranslateErrorGetEndNode getEndNode, @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode, @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @Cached TruffleStringBuilder.AppendStringNode appendStringNode, @Cached TruffleStringBuilder.AppendCodePointNode appendCodePointNode, @@ -512,7 +508,7 @@ static Object doEncode(PBaseException exception, appendCodePointNode.execute(tsb, '}'); } else { int len = BytesUtils.unicodeNonAsciiEscape(cp, 0, buf, true); - appendStringNode.execute(tsb, switchEncodingNode.execute(fromByteArrayNode.execute(buf, 0, len, Encoding.US_ASCII, true), TS_ENCODING)); + appendStringNode.execute(tsb, fromByteArrayNode.execute(buf, 0, len, TruffleString.CompactionLevel.S1, true)); } } return PFactory.createTuple(language, new Object[]{toStringNode.execute(tsb), end}); 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 8de40f873c..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 @@ -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 @@ -81,13 +81,14 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; 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.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; @@ -96,6 +97,8 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckNode; +import com.oracle.graal.python.lib.PyDateCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; @@ -112,6 +115,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaStringNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; @@ -245,8 +249,9 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString repr(Object self) { - PDate date = DateNodes.AsManagedDateNode.executeUncached(self); + static TruffleString repr(Object self, + @Bind Node inliningTarget) { + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, self); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self)); var string = String.format("%s(%d, %d, %d)", typeName, date.year, date.month, date.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); @@ -259,8 +264,9 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString str(Object self) { - PDate date = DateNodes.AsManagedDateNode.executeUncached(self); + static TruffleString str(Object self, + @Bind Node inliningTarget) { + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, self); var string = String.format("%04d-%02d-%02d", date.year, date.month, date.day); return TruffleString.FromJavaStringNode.getUncached().execute(string, TS_ENCODING); } @@ -274,9 +280,9 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached DateNodes.AsManagedDateNode asManagedDateNode, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached GetClassNode getClassNode) { - PDate date = asManagedDateNode.execute(inliningTarget, self); + DateValue date = readDateValueNode.execute(inliningTarget, self); byte[] bytes = new byte[]{(byte) (date.year / 256), (byte) (date.year % 256), (byte) date.month, (byte) date.day}; PBytes string = PFactory.createBytes(language, bytes); PTuple arguments = PFactory.createTuple(language, new Object[]{string}); @@ -292,11 +298,11 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(Object selfObj, Object otherObj, RichCmpOp op, @Bind Node inliningTarget, - @Cached DateNodes.DateCheckNode dateCheckNode, - @Cached DateNodes.AsManagedDateNode asManagedDateNode) { + @Cached PyDateCheckNode dateCheckNode, + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { if (dateCheckNode.execute(inliningTarget, selfObj) && dateCheckNode.execute(inliningTarget, otherObj)) { - PDate self = asManagedDateNode.execute(inliningTarget, selfObj); - PDate other = asManagedDateNode.execute(inliningTarget, otherObj); + DateValue self = readDateValueNode.execute(inliningTarget, selfObj); + DateValue other = readDateValueNode.execute(inliningTarget, otherObj); int result = self.compareTo(other); return op.compareResultToBool(result); } @@ -313,8 +319,8 @@ static long hash(VirtualFrame frame, Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, - @Cached DateNodes.AsManagedDateNode asManaged) { - PDate d = asManaged.execute(inliningTarget, self); + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + DateValue d = readDateValueNode.execute(inliningTarget, self); var content = new int[]{d.year, d.month, d.day}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); } @@ -341,21 +347,21 @@ static Object add(VirtualFrame frame, Object left, Object right, @TruffleBoundary private static Object addBoundary(Object left, Object right, Node inliningTarget) { Object dateObj, deltaObj; - if (DateNodes.DateCheckNode.executeUncached(left)) { - if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (PyDateCheckNode.executeUncached(left)) { + if (PyDeltaCheckNode.executeUncached(right)) { dateObj = left; deltaObj = right; } else { return PNotImplemented.NOT_IMPLEMENTED; } - } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left)) { + } else if (PyDeltaCheckNode.executeUncached(left)) { dateObj = right; deltaObj = left; } else { return PNotImplemented.NOT_IMPLEMENTED; } - PDate date = DateNodes.AsManagedDateNode.executeUncached(dateObj); - PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(deltaObj); + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, dateObj); + TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); @@ -372,7 +378,7 @@ private static Object addBoundary(Object left, Object right, Node inliningTarget LocalDate localDate = ChronoUnit.DAYS.addTo(from, days - 1); return DateNodes.SubclassNewNode.getUncached().execute(inliningTarget, - GetClassNode.executeUncached(dateObj), + getResultDateType(dateObj), localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()); @@ -399,29 +405,29 @@ static Object sub(VirtualFrame frame, Object left, Object right, @TruffleBoundary private static Object subBoundary(Object left, Object right, Node inliningTarget) { - if (!DateNodes.DateCheckNode.executeUncached(left)) { + if (!PyDateCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PDate date = DateNodes.AsManagedDateNode.executeUncached(left); - if (DateNodes.DateCheckNode.executeUncached(right)) { + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, left); + if (PyDateCheckNode.executeUncached(right)) { LocalDate from = LocalDate.of(1, 1, 1); LocalDate toSelf = LocalDate.of(date.year, date.month, date.day); long daysSelf = ChronoUnit.DAYS.between(from, toSelf) + 1; - PDate other = DateNodes.AsManagedDateNode.executeUncached(right); + DateValue other = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, right); LocalDate toOther = LocalDate.of(other.year, other.month, other.day); long daysOther = ChronoUnit.DAYS.between(from, toOther) + 1; long days = daysSelf - daysOther; return new PTimeDelta( PythonBuiltinClassType.PTimeDelta, - PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(null)), + PythonBuiltinClassType.PTimeDelta.getInstanceShape(PythonLanguage.get(inliningTarget)), (int) days, 0, 0); } - if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { - PTimeDelta timeDelta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + if (PyDeltaCheckNode.executeUncached(right)) { + TimeDeltaValue timeDelta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(date.year, date.month, date.day); long days = ChronoUnit.DAYS.between(from, to) + 1; @@ -437,7 +443,7 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget LocalDate localDate = ChronoUnit.DAYS.addTo(from, days - 1); return DateNodes.SubclassNewNode.getUncached().execute(inliningTarget, - GetClassNode.executeUncached(left), + getResultDateType(left), localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()); @@ -456,9 +462,15 @@ static int getYear(PDate self) { } @Specialization - static int getYear(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.AsManagedDateNode.getYear(self, readNode); + static int getYear(PythonAbstractNativeObject self) { + return DateNodes.FromNative.getYear(self); + } + + @Specialization + static int getYear(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + return readDateValueNode.execute(inliningTarget, self).year; } } @@ -472,9 +484,15 @@ static int getMonth(PDate self) { } @Specialization - static int getMonth(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.AsManagedDateNode.getMonth(self, readNode); + static int getMonth(PythonAbstractNativeObject self) { + return DateNodes.FromNative.getMonth(self); + } + + @Specialization + static int getMonth(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + return readDateValueNode.execute(inliningTarget, self).month; } } @@ -488,12 +506,22 @@ static int getDay(PDate self) { } @Specialization - static int getDay(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.AsManagedDateNode.getDay(self, readNode); + static int getDay(PythonAbstractNativeObject self) { + return DateNodes.FromNative.getDay(self); + } + + @Specialization + static int getDay(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateValue readDateValueNode) { + return readDateValueNode.execute(inliningTarget, self).day; } } + private static Object getResultDateType(Object dateObj) { + return IsForeignObjectNode.executeUncached(dateObj) ? PythonBuiltinClassType.PDate : GetClassNode.executeUncached(dateObj); + } + @Builtin(name = "today", minNumOfPositionalArgs = 1, isClassmethod = true, parameterNames = {"self"}) @GenerateNodeFactory public abstract static class TodayNode extends PythonBuiltinNode { @@ -738,11 +766,11 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object self, Object yearObject, Object monthObject, Object dayObject, @Bind Node inliningTarget, - @Cached DateNodes.AsManagedDateNode asManagedDateNode, + @Cached TemporalValueNodes.GetDateValue readDateValueNode, @Cached PyLongAsLongNode longAsLongNode, @Cached GetClassNode getClassNode, @Cached DateNodes.NewNode newNode) { - PDate date = asManagedDateNode.execute(inliningTarget, self); + DateValue date = readDateValueNode.execute(inliningTarget, self); final int year, month, day; if (yearObject instanceof PNone) { @@ -773,8 +801,9 @@ public abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static long toOrdinal(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static long toOrdinal(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(self.year, self.month, self.day); return ChronoUnit.DAYS.between(from, to) + 1; @@ -824,8 +853,9 @@ public abstract static class WeekDayNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static int weekDay(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static int weekDay(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DayOfWeek dayOfWeek = localDate.getDayOfWeek(); @@ -840,8 +870,9 @@ public abstract static class IsoWeekDayNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static int weekDay(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static int weekDay(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DayOfWeek dayOfWeek = localDate.getDayOfWeek(); return dayOfWeek.getValue(); @@ -855,8 +886,9 @@ public abstract static class IsoCalendarNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static PTuple isoCalendar(Object selfObj, - @Bind PythonLanguage language) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + @Bind PythonLanguage language, + @Bind Node inliningTarget) { + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); // use week based year ISO-8601 calendar @@ -874,8 +906,9 @@ public abstract static class IsoFormatNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString isoFormat(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static TruffleString isoFormat(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate locaDate = LocalDate.of(self.year, self.month, self.day); var isoString = locaDate.toString(); return TruffleString.FromJavaStringNode.getUncached().execute(isoString, TS_ENCODING); @@ -888,8 +921,9 @@ public abstract static class CTimeNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString cTime(Object selfObj) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static TruffleString cTime(Object selfObj, + @Bind Node inliningTarget) { + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE LLL ppd 00:00:00 yyyy"); String ctime = localDate.format(formatter); @@ -904,8 +938,9 @@ public abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static PTuple timeTuple(Object selfObj, - @Bind PythonLanguage language) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + @Bind PythonLanguage language, + @Bind Node inliningTarget) { + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); // Python's day of week is in range 0-6 @@ -929,8 +964,9 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization @TruffleBoundary - static TruffleString strftime(Object selfObj, TruffleString format) { - PDate self = DateNodes.AsManagedDateNode.executeUncached(selfObj); + static TruffleString strftime(Object selfObj, TruffleString format, + @Bind Node inliningTarget) { + DateValue self = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. // construct time_tuple 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 b13780c403..ad8fb02f47 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 @@ -40,19 +40,21 @@ */ package com.oracle.graal.python.builtins.modules.datetime; -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.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; @@ -62,10 +64,10 @@ 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.nodes.object.BuiltinClassProfiles; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; 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; import com.oracle.truffle.api.dsl.GenerateCached; @@ -131,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); @@ -142,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 CApiTransitions.PythonToNativeNode toNativeNode, - @Cached CApiTransitions.NativeToPythonTransferNode fromNativeNode) { + @Cached ExternalFunctionNodes.PyObjectCheckFunctionResultNode checkFunctionResultNode, + @Cached CApiTransitions.PythonToNativeInternalNode toNativeNode, + @Cached CApiTransitions.NativeToPythonInternalNode 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.execute(inliningTarget, 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.executeTransfer(inliningTarget, nativeResult)); + } finally { + Reference.reachabilityFence(cls); + } } } } @@ -185,78 +195,23 @@ static boolean isBuiltinClass(Object cls) { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class AsManagedDateNode extends Node { - public abstract PDate execute(Node inliningTarget, Object obj); - - public static PDate executeUncached(Object obj) { - return DateNodesFactory.AsManagedDateNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static PDate asManaged(PDate obj) { - return obj; - } - - @Specialization(guards = "checkNode.execute(inliningTarget, obj)", limit = "1") - static PDate asManagedNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject obj, - @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached DateCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readByteNode) { - int year = getYear(obj, readByteNode); - int month = getMonth(obj, readByteNode); - int day = getDay(obj, readByteNode); - PythonBuiltinClassType cls = PythonBuiltinClassType.PDate; - return new PDate(cls, cls.getInstanceShape(language), year, month, day); - } - - 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); + public static final class FromNative { + 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); - } - - @Fallback - static PDate error(Object obj, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "date", obj); + static int getDay(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Date__data); + return NativeMemory.readByteArrayElement(ptr, 3) & 0xFF; } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class DateCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return DateNodesFactory.DateCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PDate value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PDate); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } } 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 2e9ba79e03..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 @@ -100,13 +100,16 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins.WarnNode; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateTimeValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; 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.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; @@ -117,9 +120,14 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckNode; +import com.oracle.graal.python.lib.PyDateCheckNode; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; +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; @@ -133,6 +141,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaDoubleNode; import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode; @@ -247,7 +256,7 @@ private static Object tryToDeserializeDateTime(Object cls, Object bytesObject, O } if (naiveBytesCheck(bytes)) { - if (tzInfo != PNone.NO_VALUE && !TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (tzInfo != PNone.NO_VALUE && !PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.BAD_TZINFO_STATE_ARG); } @@ -366,7 +375,7 @@ static Object nowInTimeZone(VirtualFrame frame, Object cls, Object tzInfo, @TruffleBoundary private static Object nowInTimeZoneBoundary(Object cls, Object tzInfo, Node inliningTarget) { - if (!TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (!PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfo); } // convert current time in UTC to the given time zone with tzinfo.fromutc() @@ -407,11 +416,12 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString repr(Object selfObj, - @Bind Node inliningTarget) { + @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode) { EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); try { - return reprBoundary(selfObj); + return reprBoundary(inliningTarget, selfObj, tzInfoNode.execute(inliningTarget, selfObj)); } finally { // Some uncached nodes (e.g. PyFloatAsDoubleNode, PyLongAsLongNode, // PyObjectReprAsObjectNode) may raise exceptions that are not @@ -421,8 +431,8 @@ static TruffleString repr(Object selfObj, } @TruffleBoundary - private static TruffleString reprBoundary(Object selfObj) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + private static TruffleString reprBoundary(Node inliningTarget, Object selfObj, Object tzInfo) { + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); @@ -438,10 +448,10 @@ private static TruffleString reprBoundary(Object selfObj) { builder.append(", fold=1"); } - if (self.tzInfo != null) { + if (tzInfo != null) { builder.append(", tzinfo="); - Object tzinfoReprObject = PyObjectReprAsObjectNode.executeUncached(self.tzInfo); + Object tzinfoReprObject = PyObjectReprAsObjectNode.executeUncached(tzInfo); String tzinfoRepr = CastToJavaStringNode.getUncached().execute(tzinfoReprObject); builder.append(tzinfoRepr); } @@ -459,8 +469,10 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + Object tzInfo = tzInfoNode.execute(inliningTarget, selfObj); // DateTime is serialized in the following format: // ( // bytes(year 1st byte, year 2nd byte, month, day, hours, minutes, seconds, microseconds @@ -484,8 +496,8 @@ static Object reduce(Object selfObj, PBytes baseState = PFactory.createBytes(language, baseStateBytes); final PTuple arguments; - if (self.tzInfo != null) { - arguments = PFactory.createTuple(language, new Object[]{baseState, self.tzInfo}); + if (tzInfo != null) { + arguments = PFactory.createTuple(language, new Object[]{baseState, tzInfo}); } else { arguments = PFactory.createTuple(language, new Object[]{baseState}); } @@ -502,8 +514,10 @@ public abstract static class ReduceExNode extends PythonBinaryBuiltinNode { static Object reduceEx(Object selfObj, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + Object tzInfo = tzInfoNode.execute(inliningTarget, selfObj); byte[] baseStateBytes = new byte[10]; baseStateBytes[0] = (byte) (self.year / 256); baseStateBytes[1] = (byte) (self.year % 256); @@ -523,8 +537,8 @@ static Object reduceEx(Object selfObj, int protocol, PBytes baseState = PFactory.createBytes(language, baseStateBytes); final PTuple arguments; - if (self.tzInfo != null) { - arguments = PFactory.createTuple(language, new Object[]{baseState, self.tzInfo}); + if (tzInfo != null) { + arguments = PFactory.createTuple(language, new Object[]{baseState, tzInfo}); } else { arguments = PFactory.createTuple(language, new Object[]{baseState}); } @@ -541,10 +555,11 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp op, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return richCmpBoundary(self, other, op, inliningTarget); + return richCmpBoundary(self, other, op, inliningTarget, tzInfoNode); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -553,8 +568,8 @@ static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp o } @TruffleBoundary - private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget) { - if (!DateTimeNodes.DateTimeCheckNode.executeUncached(otherObj)) { + private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget, DateTimeNodes.TzInfoNode tzInfoNode) { + if (!PyDateTimeCheckNode.executeUncached(otherObj)) { /* * Prevent invocation of date_richcompare. We want to return NotImplemented here to * give the other object a chance. But since DateTime is a subclass of Date, if the @@ -562,7 +577,7 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp * alone, and we don't want that. So force unequal or uncomparable here in that * case. */ - if (DateNodes.DateCheckNode.executeUncached(otherObj)) { + if (PyDateCheckNode.executeUncached(otherObj)) { if (op == RichCmpOp.Py_EQ) { return false; } else if (op == RichCmpOp.Py_NE) { @@ -573,23 +588,25 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp } return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); - PDateTime other = DateTimeNodes.AsManagedDateTimeNode.executeUncached(otherObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + DateTimeValue other = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, otherObj); + Object selfTzInfo = tzInfoNode.execute(inliningTarget, selfObj); + Object otherTzInfo = tzInfoNode.execute(inliningTarget, otherObj); // either naive datetimes (without timezone) or timezones are exactly the same objects - if (self.tzInfo == other.tzInfo) { + if (selfTzInfo == otherTzInfo) { int result = compareDateTimeComponents(self, other); return op.compareResultToBool(result); } - PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, inliningTarget); - PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, inliningTarget); + PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(selfTzInfo, selfObj, inliningTarget); + PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(otherTzInfo, otherObj, inliningTarget); if (Objects.equals(selfUtcOffset, otherUtcOffset)) { int result = compareDateTimeComponents(self, other); if (result == 0 && (op == RichCmpOp.Py_EQ || op == RichCmpOp.Py_NE) && selfUtcOffset != null) { // if any utc offset is affected by a fold value - return false - if (isExceptionInPep495(selfObj, self, selfUtcOffset, other, otherUtcOffset, inliningTarget)) { + if (isExceptionInPep495(selfObj, self, selfTzInfo, selfUtcOffset, otherObj, other, otherTzInfo, otherUtcOffset, inliningTarget)) { result = 1; } } @@ -618,7 +635,7 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp if (result == 0 && (op == RichCmpOp.Py_EQ || op == RichCmpOp.Py_NE)) { // if any utc offset is affected by a fold value - return false - if (isExceptionInPep495(selfObj, self, selfUtcOffset, other, otherUtcOffset, inliningTarget)) { + if (isExceptionInPep495(selfObj, self, selfTzInfo, selfUtcOffset, otherObj, other, otherTzInfo, otherUtcOffset, inliningTarget)) { result = 1; } } @@ -627,7 +644,7 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp } @TruffleBoundary - private static int compareDateTimeComponents(PDateTime self, PDateTime other) { + private static int compareDateTimeComponents(DateTimeValue self, DateTimeValue other) { // compare only year, month, day, hours, minutes, ... and ignore fold int[] selfComponents = new int[]{self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond}; int[] otherComponents = new int[]{other.year, other.month, other.day, other.hour, other.minute, other.second, other.microsecond}; @@ -640,19 +657,17 @@ private static int compareDateTimeComponents(PDateTime self, PDateTime other) { * 495 – Local Time Disambiguation". See PEP 495 * – Local Time Disambiguation */ - private static boolean isExceptionInPep495(Object selfObj, PDateTime self, PTimeDelta selfUtcOffset, PDateTime other, PTimeDelta otherUtcOffset, Node inliningTarget) { - return isExceptionInPep495(selfObj, self, selfUtcOffset, inliningTarget) || isExceptionInPep495(selfObj, other, otherUtcOffset, inliningTarget); + private static boolean isExceptionInPep495(Object selfObj, DateTimeValue self, Object selfTzInfo, PTimeDelta selfUtcOffset, Object otherObj, DateTimeValue other, Object otherTzInfo, + PTimeDelta otherUtcOffset, Node inliningTarget) { + return isExceptionInPep495(selfObj, self, selfTzInfo, selfUtcOffset, inliningTarget) || isExceptionInPep495(otherObj, other, otherTzInfo, otherUtcOffset, inliningTarget); } - @TruffleBoundary - private static boolean isExceptionInPep495(Object dateTimeObj, PDateTime dateTime, PTimeDelta utcOffset, Node inliningTarget) { - Object cls = GetClassNode.executeUncached(dateTimeObj); - Shape shape = TypeNodes.GetInstanceShape.getUncached().execute(cls); + private static boolean isExceptionInPep495(Object dateTimeObj, DateTimeValue dateTime, Object tzInfo, PTimeDelta utcOffset, Node inliningTarget) { int fold = dateTime.fold == 1 ? 0 : 1; - - PDateTime newDateTime = new PDateTime(cls, shape, dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, - dateTime.microsecond, dateTime.tzInfo, fold); - PTimeDelta newUtcOffset = DatetimeModuleBuiltins.callUtcOffset(newDateTime.tzInfo, newDateTime, inliningTarget); + Shape shape = PythonBuiltinClassType.PDateTime.getInstanceShape(PythonLanguage.get(inliningTarget)); + Object newDateTime = new PDateTime(PythonBuiltinClassType.PDateTime, shape, dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, + dateTime.microsecond, tzInfo, fold); + PTimeDelta newUtcOffset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, newDateTime, inliningTarget); return !utcOffset.equals(newUtcOffset); } @@ -665,26 +680,27 @@ abstract static class HashNode extends HashBuiltinNode { @Specialization static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, - @Cached PRaiseNode raiseNode, - @Cached TypeNodes.GetInstanceShape getInstanceShape) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + @Cached PRaiseNode raiseNode) { + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + Object tzInfo = tzInfoNode.execute(inliningTarget, selfObj); final PTimeDelta offset; - if (self.tzInfo == null) { + if (tzInfo == null) { offset = null; } else { // ignore fold in calculating utc offset - final PDateTime getUtcOffsetFrom; + final Object getUtcOffsetFrom; if (self.fold == 1) { // reset fold - Object cls = GetClassNode.executeUncached(selfObj); - Shape shape = getInstanceShape.execute(cls); - getUtcOffsetFrom = new PDateTime(cls, shape, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, self.tzInfo, 0); + Shape shape = PythonBuiltinClassType.PDateTime.getInstanceShape(PythonLanguage.get(inliningTarget)); + getUtcOffsetFrom = new PDateTime(PythonBuiltinClassType.PDateTime, shape, self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond, + tzInfo, 0); } else { - getUtcOffsetFrom = self; + getUtcOffsetFrom = selfObj; } - offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, getUtcOffsetFrom, frame, inliningTarget, callMethodObjArgs, raiseNode); + offset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, getUtcOffsetFrom, frame, inliningTarget, callMethodObjArgs, raiseNode); } if (offset == null) { @@ -695,12 +711,12 @@ static long hash(VirtualFrame frame, Object selfObj, } @TruffleBoundary - private static long getHashForDateTime(PDateTime self) { + private static long getHashForDateTime(DateTimeValue self) { return Objects.hash(self.year, self.month, self.day, self.hour, self.minute, self.second, self.microsecond); } @TruffleBoundary - private static long getHashForDateTimeWithOffset(PDateTime self, PTimeDelta offset) { + private static long getHashForDateTimeWithOffset(DateTimeValue self, PTimeDelta offset) { LocalDateTime utc = subtractOffsetFromDateTime(self, offset); return Objects.hash(utc.getYear(), utc.getMonthValue(), utc.getDayOfMonth(), utc.getHour(), utc.getMinute(), utc.getSecond(), utc.getNano() / 1_000); } @@ -713,10 +729,13 @@ abstract static class AddNode extends BinaryOpBuiltinNode { @Specialization static Object add(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached GetClassNode getClassNode, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return addBoundary(left, right, inliningTarget); + return addBoundary(left, right, inliningTarget, isForeignObjectNode, getClassNode, tzInfoNode); } finally { // A Python method call (using DateTimeNodes.SubclassNewNode) should be // connected to a current node. @@ -725,32 +744,34 @@ static Object add(VirtualFrame frame, Object left, Object right, } @TruffleBoundary - private static Object addBoundary(Object left, Object right, Node inliningTarget) { + private static Object addBoundary(Object left, Object right, Node inliningTarget, IsForeignObjectNode isForeignObjectNode, GetClassNode getClassNode, + DateTimeNodes.TzInfoNode tzInfoNode) { Object dateTimeObj, deltaObj; - if (DateTimeNodes.DateTimeCheckNode.executeUncached(left)) { - if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (PyDateTimeCheckNode.executeUncached(left)) { + if (PyDeltaCheckNode.executeUncached(right)) { dateTimeObj = left; deltaObj = right; } else { return PNotImplemented.NOT_IMPLEMENTED; } - } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left)) { + } else if (PyDeltaCheckNode.executeUncached(left)) { dateTimeObj = right; deltaObj = left; } else { return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime date = DateTimeNodes.AsManagedDateTimeNode.executeUncached(dateTimeObj); - PTimeDelta delta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(deltaObj); + DateTimeValue date = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTimeObj); + TimeDeltaValue delta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, deltaObj); - LocalDateTime local = toLocalDateTime(date); + LocalDateTime local = date.toLocalDateTime(); LocalDateTime localAdjusted = local.plusDays(delta.days).plusSeconds(delta.seconds).plusNanos(delta.microseconds * 1_000L); if (localAdjusted.getYear() < MIN_YEAR || localAdjusted.getYear() > MAX_YEAR) { throw PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); } - return toPDateTime(localAdjusted, date.tzInfo, date.fold, inliningTarget, GetClassNode.executeUncached(dateTimeObj)); + Object tzInfo = tzInfoNode.execute(inliningTarget, dateTimeObj); + return toPDateTime(localAdjusted, tzInfo, date.fold, inliningTarget, getResultDateTimeType(dateTimeObj, inliningTarget, isForeignObjectNode, getClassNode)); } } @@ -761,10 +782,13 @@ abstract static class SubNode extends BinaryOpBuiltinNode { @Specialization static Object sub(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached GetClassNode getClassNode, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return subBoundary(left, right, inliningTarget); + return subBoundary(left, right, inliningTarget, isForeignObjectNode, getClassNode, tzInfoNode); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -773,19 +797,19 @@ static Object sub(VirtualFrame frame, Object left, Object right, } @TruffleBoundary - private static Object subBoundary(Object left, Object right, Node inliningTarget) { - if (!DateTimeNodes.DateTimeCheckNode.executeUncached(left)) { + private static Object subBoundary(Object left, Object right, Node inliningTarget, IsForeignObjectNode isForeignObjectNode, GetClassNode getClassNode, + DateTimeNodes.TzInfoNode tzInfoNode) { + if (!PyDateTimeCheckNode.executeUncached(left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(left); - if (DateTimeNodes.DateTimeCheckNode.executeUncached(right)) { - PDateTime other = DateTimeNodes.AsManagedDateTimeNode.executeUncached(right); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, left); + Object selfTzInfo = tzInfoNode.execute(inliningTarget, left); + if (PyDateTimeCheckNode.executeUncached(right)) { + DateTimeValue other = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, right); + Object otherTzInfo = tzInfoNode.execute(inliningTarget, right); - final PTimeDelta selfOffset; - final PTimeDelta otherOffset; - - selfOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, inliningTarget); - otherOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, other, inliningTarget); + final PTimeDelta selfOffset = DatetimeModuleBuiltins.callUtcOffset(selfTzInfo, left, inliningTarget); + final PTimeDelta otherOffset = DatetimeModuleBuiltins.callUtcOffset(otherTzInfo, right, inliningTarget); if ((selfOffset == null) != (otherOffset == null)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CANNOT_SUBTRACT_OFFSET_NAIVE_AND_OFFSET_AWARE_DATETIMES); @@ -794,12 +818,12 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget final LocalDateTime selfToCompare; final LocalDateTime otherToCompare; - if (selfOffset != null && self.tzInfo != other.tzInfo) { + if (selfOffset != null && selfTzInfo != otherTzInfo) { selfToCompare = subtractOffsetFromDateTime(self, selfOffset); otherToCompare = subtractOffsetFromDateTime(other, otherOffset); } else { - selfToCompare = toLocalDateTime(self); - otherToCompare = toLocalDateTime(other); + selfToCompare = self.toLocalDateTime(); + otherToCompare = other.toLocalDateTime(); } long selfSeconds = selfToCompare.toEpochSecond(ZoneOffset.UTC); @@ -814,16 +838,16 @@ private static Object subBoundary(Object left, Object right, Node inliningTarget 0, 0, 0); - } else if (TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { - PTimeDelta timeDelta = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); - LocalDateTime local = toLocalDateTime(self); + } else if (PyDeltaCheckNode.executeUncached(right)) { + TimeDeltaValue timeDelta = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); + LocalDateTime local = self.toLocalDateTime(); LocalDateTime localAdjusted = local.minusDays(timeDelta.days).minusSeconds(timeDelta.seconds).minusNanos(timeDelta.microseconds * 1_000L); if (localAdjusted.getYear() < MIN_YEAR || localAdjusted.getYear() > MAX_YEAR) { throw PRaiseNode.raiseStatic(inliningTarget, OverflowError, ErrorMessages.DATE_VALUE_OUT_OF_RANGE); } - return toPDateTime(localAdjusted, self.tzInfo, self.fold, inliningTarget, GetClassNode.executeUncached(left)); + return toPDateTime(localAdjusted, selfTzInfo, self.fold, inliningTarget, getResultDateTimeType(left, inliningTarget, isForeignObjectNode, getClassNode)); } else { return PNotImplemented.NOT_IMPLEMENTED; } @@ -839,9 +863,15 @@ static int getHour(PDateTime self) { } @Specialization - static int getHour(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readByteNode) { - return DateTimeNodes.AsManagedDateTimeNode.getHour(self, readByteNode); + static int getHour(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getHour(self); + } + + @Specialization + static int getHour(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).hour; } } @@ -855,9 +885,15 @@ static int getMinute(PDateTime self) { } @Specialization - static int getMinute(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.AsManagedDateTimeNode.getMinute(self, readNode); + static int getMinute(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getMinute(self); + } + + @Specialization + static int getMinute(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).minute; } } @@ -871,9 +907,15 @@ static int getSecond(PDateTime self) { } @Specialization - static int getSecond(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.AsManagedDateTimeNode.getSecond(self, readNode); + static int getSecond(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getSecond(self); + } + + @Specialization + static int getSecond(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).second; } } @@ -887,9 +929,15 @@ static int getMicrosecond(PDateTime self) { } @Specialization - static int getMicrosecond(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.AsManagedDateTimeNode.getMicrosecond(self, readNode); + static int getMicrosecond(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getMicrosecond(self); + } + + @Specialization + static int getMicrosecond(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).microsecond; } } @@ -916,9 +964,15 @@ static int getFold(PDateTime self) { } @Specialization - static int getFold(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.AsManagedDateTimeNode.getFold(self, readNode); + static int getFold(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getFold(self); + } + + @Specialization + static int getFold(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + return readDateTimeValueNode.execute(inliningTarget, self).fold; } } @@ -974,7 +1028,7 @@ private static Object fromTimestampBoundary(Object cls, Object timestampObject, if (tzInfoObject instanceof PNone) { tzInfo = null; } else { - if (!TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfoObject)) { + if (!PyTZInfoCheckNode.executeUncached(tzInfoObject)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfoObject); } @@ -1147,8 +1201,9 @@ public abstract static class CombineNode extends PythonBuiltinNode { static Object combine(Object cls, Object dateObject, Object timeObject, Object tzInfoObject, @Bind Node inliningTarget, @Cached PRaiseNode raiseNode, + @Cached TimeNodes.TzInfoNode timeTzInfoNode, @Cached DateTimeNodes.SubclassNewNode newNode) { - if (!DateNodes.DateCheckNode.executeUncached(dateObject)) { + if (!PyDateCheckNode.executeUncached(dateObject)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, @@ -1158,7 +1213,7 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t dateObject); } - if (!TimeNodes.TimeCheckNode.executeUncached(timeObject)) { + if (!PyTimeCheckNode.executeUncached(timeObject)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ARG_D_MUST_BE_S_NOT_P, @@ -1168,17 +1223,17 @@ static Object combine(Object cls, Object dateObject, Object timeObject, Object t timeObject); } - PDate date = DateNodes.AsManagedDateNode.executeUncached(dateObject); - PTime time = TimeNodes.AsManagedTimeNode.executeUncached(timeObject); + DateValue date = TemporalValueNodes.GetDateValue.executeUncached(inliningTarget, dateObject); + TimeValue time = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, timeObject); final Object tzInfo; if (tzInfoObject instanceof PNone) { - tzInfo = time.tzInfo; + tzInfo = timeTzInfoNode.execute(inliningTarget, timeObject); } else { tzInfo = tzInfoObject; } - if (tzInfo != null && !TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (tzInfo != null && !PyTZInfoCheckNode.executeUncached(tzInfo)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, @@ -2310,22 +2365,14 @@ private static Object parse(String string, String format, PythonContext context, TimeZone timeZone = TimeModuleBuiltins.getGlobalTimeZone(context); String zoneName = timeZone.getDisplayName(false, TimeZone.SHORT); String zoneNameDaylightSaving = timeZone.getDisplayName(true, TimeZone.SHORT); + String matchedZoneName = matchTimeZoneName(string, i, zoneName, zoneNameDaylightSaving, "UTC", "GMT"); - if (string.startsWith("UTC", i)) { - builder.setTimeZoneName("UTC"); - i += 3; - } else if (string.startsWith("GMT", i)) { - builder.setTimeZoneName("GMT"); - i += 3; - } else if (string.startsWith(zoneName, i)) { - builder.setTimeZoneName(zoneName); - i += zoneName.length(); - } else if (string.startsWith(zoneNameDaylightSaving, i)) { - builder.setTimeZoneName(zoneNameDaylightSaving); - i += zoneNameDaylightSaving.length(); - } else { + if (matchedZoneName == null) { throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ErrorMessages.TIME_DATA_S_DOES_NOT_MATCH_FORMAT_S, string, format); } + + builder.setTimeZoneName(matchedZoneName); + i += matchedZoneName.length(); } case 'j' -> { var pos = new ParsePosition(i); @@ -2487,6 +2534,16 @@ private static Integer parseDigits(String source, int from, int digitsCount) { return result; } + private static String matchTimeZoneName(String string, int from, String... candidates) { + String matched = null; + for (String candidate : candidates) { + if (candidate != null && string.startsWith(candidate, from) && (matched == null || candidate.length() > matched.length())) { + matched = candidate; + } + } + return matched; + } + @TruffleBoundary private static Integer parseDigitsUpTo(String source, ParsePosition from, int maxDigitsCount) { int result = 0; @@ -2525,7 +2582,7 @@ abstract static class DateNode extends PythonUnaryBuiltinNode { static Object getDate(Object selfObj, @Bind Node inliningTarget, @Cached DateNodes.NewNode newDateNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newDateNode.execute(inliningTarget, PythonBuiltinClassType.PDate, self.year, @@ -2542,7 +2599,7 @@ abstract static class TimeNode extends PythonUnaryBuiltinNode { static Object getTime(Object selfObj, @Bind Node inliningTarget, @Cached TimeNodes.NewNode newTimeNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, self.hour, @@ -2561,15 +2618,16 @@ abstract static class TimeTzNode extends PythonUnaryBuiltinNode { @Specialization static Object getTime(Object selfObj, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached TimeNodes.NewNode newTimeNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); return newTimeNode.execute(inliningTarget, PythonBuiltinClassType.PTime, self.hour, self.minute, self.second, self.microsecond, - self.tzInfo, + tzInfoNode.execute(inliningTarget, selfObj), self.fold); } } @@ -2582,10 +2640,12 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Object monthObject, Object dayObject, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached PyLongAsLongNode asLongNode, @Cached GetClassNode getClassNode, @Cached DateTimeNodes.NewNode newDateTimeNode) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final long year, month, day; if (yearObject instanceof PNone) { @@ -2634,7 +2694,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj } if (tzInfoObject == PNone.NO_VALUE) { - tzInfo = self.tzInfo; + tzInfo = tzInfoNode.execute(inliningTarget, selfObj); } else if (tzInfoObject == PNone.NONE) { tzInfo = null; } else { @@ -2647,7 +2707,7 @@ static Object replace(VirtualFrame frame, Object selfObj, Object yearObject, Obj fold = asLongNode.execute(frame, inliningTarget, foldObject); } - Object type = getClassNode.execute(inliningTarget, selfObj); + Object type = getResultDateTimeType(selfObj, inliningTarget, isForeignObjectNode, getClassNode); return newDateTimeNode.execute(inliningTarget, type, year, month, day, hour, minute, second, microsecond, tzInfo, fold); } } @@ -2659,10 +2719,13 @@ abstract static class AsTimeZoneNode extends PythonBinaryBuiltinNode { @Specialization static Object inTimeZone(VirtualFrame frame, Object self, Object tzInfo, @Bind Node inliningTarget, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached GetClassNode getClassNode, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return inTimeZoneBoundary(self, tzInfo, inliningTarget); + return inTimeZoneBoundary(self, tzInfo, inliningTarget, getResultDateTimeType(self, inliningTarget, isForeignObjectNode, getClassNode), tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset // and PyObjectCallMethodObjArgs) should be connected to a current node. @@ -2671,24 +2734,24 @@ static Object inTimeZone(VirtualFrame frame, Object self, Object tzInfo, } @TruffleBoundary - private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inliningTarget) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); - if (tzInfo == self.tzInfo) { - return self; + private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inliningTarget, Object resultType, Object selfTzInfo) { + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + if (tzInfo == selfTzInfo) { + return selfObj; } Object sourceTimeZone; - if (self.tzInfo != null) { - sourceTimeZone = self.tzInfo; + if (selfTzInfo != null) { + sourceTimeZone = selfTzInfo; } else { - sourceTimeZone = getSystemTimeZoneAt(toLocalDateTime(self), self.fold, inliningTarget); + sourceTimeZone = getSystemTimeZoneAt(self.toLocalDateTime(), self.fold, inliningTarget); } - PTimeDelta sourceOffset = DatetimeModuleBuiltins.callUtcOffset(sourceTimeZone, self, inliningTarget); + PTimeDelta sourceOffset = DatetimeModuleBuiltins.callUtcOffset(sourceTimeZone, selfObj, inliningTarget); if (sourceOffset == null) { - sourceTimeZone = getSystemTimeZoneAt(toLocalDateTime(self), self.fold, inliningTarget); - sourceOffset = DatetimeModuleBuiltins.callUtcOffset(sourceTimeZone, self, inliningTarget); + sourceTimeZone = getSystemTimeZoneAt(self.toLocalDateTime(), self.fold, inliningTarget); + sourceOffset = DatetimeModuleBuiltins.callUtcOffset(sourceTimeZone, selfObj, inliningTarget); } LocalDateTime selfAsLocalDateTimeInUtc = subtractOffsetFromDateTime(self, sourceOffset); @@ -2698,14 +2761,14 @@ private static Object inTimeZoneBoundary(Object selfObj, Object tzInfo, Node inl final Object targetTimeZone; if (tzInfo instanceof PNone) { - targetTimeZone = getSystemTimeZoneAt(toLocalDateTime(self), self.fold, inliningTarget); - } else if (!TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + targetTimeZone = getSystemTimeZoneAt(self.toLocalDateTime(), self.fold, inliningTarget); + } else if (!PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfo); } else { targetTimeZone = tzInfo; } - Object selfInUtc = toPDateTime(selfAsLocalDateTimeInUtc, targetTimeZone, 0, inliningTarget, GetClassNode.executeUncached(selfObj)); + Object selfInUtc = toPDateTime(selfAsLocalDateTimeInUtc, targetTimeZone, 0, inliningTarget, resultType); return PyObjectCallMethodObjArgs.executeUncached(targetTimeZone, T_FROMUTC, selfInUtc); } @@ -2836,10 +2899,11 @@ public abstract static class TimeTupleNode extends PythonUnaryBuiltinNode { static PTuple composeTimeTuple(VirtualFrame frame, Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return composeTimeTupleBoundary(self, inliningTarget, language); + return composeTimeTupleBoundary(self, inliningTarget, language, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callDst) should // be connected to a current node. @@ -2848,21 +2912,21 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, } @TruffleBoundary - private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language, Object tzInfo) { + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); LocalDate localDate = LocalDate.of(self.year, self.month, self.day); int dayOfWeek = localDate.getDayOfWeek().getValue() - 1; // Python's day of week range // is 0-6 int dayOfYear = localDate.getDayOfYear(); - int isDst = getIsDst(self, inliningTarget); + int isDst = getIsDst(tzInfo, selfObj, inliningTarget); Object[] fields = new Object[]{self.year, self.month, self.day, self.hour, self.minute, self.second, dayOfWeek, dayOfYear, isDst}; return PFactory.createStructSeq(language, TimeModuleBuiltins.STRUCT_TIME_DESC, fields); } - private static int getIsDst(PDateTime self, Node inliningTarget) { + private static int getIsDst(Object tzInfo, Object selfObj, Node inliningTarget) { int isDst; - PTimeDelta offset = DatetimeModuleBuiltins.callDst(self.tzInfo, self, inliningTarget); + PTimeDelta offset = DatetimeModuleBuiltins.callDst(tzInfo, selfObj, inliningTarget); if (offset == null) { isDst = -1; @@ -2886,10 +2950,11 @@ public abstract static class UtcTimeTupleNode extends PythonUnaryBuiltinNode { static PTuple composeTimeTuple(VirtualFrame frame, Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return composeTimeTupleBoundary(self, inliningTarget, language); + return composeTimeTupleBoundary(self, inliningTarget, language, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset // and PyObjectCallMethodObjArgs) should be connected to a current node. @@ -2898,13 +2963,13 @@ static PTuple composeTimeTuple(VirtualFrame frame, Object self, } @TruffleBoundary - private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + private static PTuple composeTimeTupleBoundary(Object selfObj, Node inliningTarget, PythonLanguage language, Object tzInfo) { + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); final LocalDateTime localDateTime; - PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, inliningTarget); + PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, selfObj, inliningTarget); if (offset == null) { - localDateTime = toLocalDateTime(self); + localDateTime = self.toLocalDateTime(); } else { // convert self to UTC localDateTime = subtractOffsetFromDateTime(self, offset); @@ -2932,7 +2997,7 @@ public abstract static class ToOrdinalNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static long toOrdinal(Object selfObj) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(null, selfObj); LocalDate from = LocalDate.of(1, 1, 1); LocalDate to = LocalDate.of(self.year, self.month, self.day); return ChronoUnit.DAYS.between(from, to) + 1; @@ -2946,10 +3011,11 @@ public abstract static class TimestampNode extends PythonUnaryBuiltinNode { @Specialization static double toTimestamp(VirtualFrame frame, Object self, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return toTimestampBoundary(self, inliningTarget); + return toTimestampBoundary(self, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -2958,14 +3024,14 @@ static double toTimestamp(VirtualFrame frame, Object self, } @TruffleBoundary - private static double toTimestampBoundary(Object selfObj, Node inliningTarget) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); - if (self.tzInfo == null) { + private static double toTimestampBoundary(Object selfObj, Node inliningTarget, Object tzInfo) { + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); + if (tzInfo == null) { // CPython: local_to_seconds() TimeZone timeZone = TimeModuleBuiltins.getGlobalTimeZone(getContext(inliningTarget)); ZoneId zoneId = timeZone.toZoneId(); - LocalDateTime localDateTime = toLocalDateTime(self); + LocalDateTime localDateTime = self.toLocalDateTime(); ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, zoneId); if (localDateTime.equals(zonedDateTime.toLocalDateTime())) { @@ -2992,10 +3058,10 @@ private static double toTimestampBoundary(Object selfObj, Node inliningTarget) { } } else { final LocalDateTime localDateTime; - PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, self, inliningTarget); + PTimeDelta offset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, selfObj, inliningTarget); if (offset == null) { - localDateTime = toLocalDateTime(self); + localDateTime = self.toLocalDateTime(); } else { // convert self to UTC localDateTime = subtractOffsetFromDateTime(self, offset); @@ -3016,10 +3082,11 @@ public abstract static class IsoFormatNode extends PythonTernaryBuiltinNode { @Specialization static TruffleString isoFormat(VirtualFrame frame, Object self, Object separatorObject, Object timespecObject, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return isoFormatBoundary(self, separatorObject, timespecObject, inliningTarget); + return isoFormatBoundary(self, separatorObject, timespecObject, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -3028,8 +3095,8 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object separator } @TruffleBoundary - private static TruffleString isoFormatBoundary(Object selfObj, Object separatorObject, Object timespecObject, Node inliningTarget) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + private static TruffleString isoFormatBoundary(Object selfObj, Object separatorObject, Object timespecObject, Node inliningTarget, Object tzInfo) { + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); String dateSection = PythonUtils.formatJString("%04d-%02d-%02d", self.year, self.month, self.day); @@ -3119,7 +3186,7 @@ private static TruffleString isoFormatBoundary(Object selfObj, Object separatorO ErrorMessages.UNKNOWN_TIMESPEC_VALUE); } - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, self, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, selfObj, true, inliningTarget); builder.append(utcOffsetString); return TruffleString.FromJavaStringNode.getUncached().execute(builder.toString(), TS_ENCODING); @@ -3133,8 +3200,8 @@ public abstract static class CTimeNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString cTime(Object selfObj) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); - LocalDateTime localDateTime = LocalDateTime.of(self.year, self.month, self.day, self.hour, self.minute, self.second); + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(null, selfObj); + LocalDateTime localDateTime = self.toLocalDateTime(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE LLL ppd HH:mm:ss yyyy"); String ctime = localDateTime.format(formatter); return TruffleString.FromJavaStringNode.getUncached().execute(ctime, TS_ENCODING); @@ -3154,10 +3221,11 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization static TruffleString strftime(VirtualFrame frame, Object self, TruffleString format, @Bind Node inliningTarget, + @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return strftimeBoundary(self, format, inliningTarget); + return strftimeBoundary(self, format, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using PyObjectCallMethodObjArgs and // DatetimeModuleBuiltins.callUtcOffset) should be connected to a @@ -3167,8 +3235,8 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for } @TruffleBoundary - private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { - PDateTime self = DateTimeNodes.AsManagedDateTimeNode.executeUncached(selfObj); + private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget, Object tzInfo) { + DateTimeValue self = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. // construct time_tuple @@ -3177,7 +3245,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form int dayOfYear = localDate.getDayOfYear(); int[] timeTuple = new int[]{self.year, self.month, self.day, self.hour, self.minute, self.second, dayOfWeek, dayOfYear, -1}; - String formatPreprocessed = preprocessFormat(format, self, inliningTarget); + String formatPreprocessed = preprocessFormat(format, self, selfObj, inliningTarget, tzInfo); return TimeModuleBuiltins.StrfTimeNode.format(formatPreprocessed, timeTuple, TruffleString.FromJavaStringNode.getUncached()); } @@ -3185,7 +3253,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form // The datetime.datetime.strftime() method supports some extra formatters - %f, %z, %:z, // and %Z so handle them here. // CPython: wrap_strftime() - private static String preprocessFormat(TruffleString tsformat, PDateTime self, Node inliningTarget) { + private static String preprocessFormat(TruffleString tsformat, DateTimeValue self, Object selfObj, Node inliningTarget, Object tzInfo) { String format = tsformat.toString(); StringBuilder builder = new StringBuilder(); int i = 0; @@ -3208,13 +3276,13 @@ private static String preprocessFormat(TruffleString tsformat, PDateTime self, N char c = format.charAt(p + 1); if (c == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, self, false, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, selfObj, false, inliningTarget); builder.append(utcOffsetString); i = p + 2; } else if (c == 'Z') { - if (self.tzInfo != null) { + if (tzInfo != null) { // call tzname() - Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(self.tzInfo, T_TZNAME, self); + Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(tzInfo, T_TZNAME, selfObj); // ignore None value if (tzNameObject != PNone.NONE) { @@ -3245,7 +3313,7 @@ private static String preprocessFormat(TruffleString tsformat, PDateTime self, N char d = format.charAt(p + 2); if (d == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, self, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, selfObj, true, inliningTarget); builder.append(utcOffsetString); i = p + 3; @@ -3261,13 +3329,13 @@ private static String preprocessFormat(TruffleString tsformat, PDateTime self, N } @TruffleBoundary - private static LocalDateTime subtractOffsetFromDateTime(PDateTime self, PTimeDelta offset) { - return toLocalDateTime(self).minusDays(offset.days).minusSeconds(offset.seconds).minusNanos(offset.microseconds * 1_000L); + private static LocalDateTime subtractOffsetFromDateTime(DateTimeValue self, PTimeDelta offset) { + return self.toLocalDateTime().minusDays(offset.days).minusSeconds(offset.seconds).minusNanos(offset.microseconds * 1_000L); } @TruffleBoundary private static LocalDateTime toLocalDateTime(PDateTime dateTime) { - return LocalDateTime.of(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.microsecond * 1_000); + return DateTimeValue.of(dateTime).toLocalDateTime(); } private static Object toPDateTime(LocalDateTime local, Object tzInfo, int fold, Node inliningTarget, Object cls) { @@ -3303,6 +3371,10 @@ private static Object toPDateTime(ZonedDateTime local, Object tzInfo, int fold, fold); } + private static Object getResultDateTimeType(Object selfObj, Node inliningTarget, IsForeignObjectNode isForeignObjectNode, GetClassNode getClassNode) { + return isForeignObjectNode.execute(inliningTarget, selfObj) ? PythonBuiltinClassType.PDateTime : getClassNode.execute(inliningTarget, selfObj); + } + /** * Check whether there was setting clocks back due to daylight saving time transition. CPython: * datetime_from_timet_and_us() 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 f6b3eddafb..7d785799af 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,31 +44,35 @@ 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.PythonLanguage; 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.PyLongAsIntNode; +import com.oracle.graal.python.lib.PyTZInfoCheckNode; 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.nodes.object.BuiltinClassProfiles; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; 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; import com.oracle.truffle.api.dsl.GenerateCached; @@ -96,7 +100,7 @@ static Object newDateTime(VirtualFrame frame, Node inliningTarget, Object cls, O Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Cached PyLongAsIntNode asIntNode, @Cached DateTimeNodes.NewUnsafeNode newUnsafeNode, - @Cached TzInfoNodes.TzInfoCheckNode tzInfoCheckNode, + @Cached PyTZInfoCheckNode tzInfoCheckNode, @Cached PRaiseNode raiseNode) { int year = asIntNode.execute(frame, inliningTarget, yearObject); int month = asIntNode.execute(frame, inliningTarget, monthObject); @@ -174,7 +178,7 @@ private static void validateDateComponents(Node inliningTarget, PRaiseNode raise } private static void validateTimeComponents(Node inliningTarget, PRaiseNode raiseNode, long hour, long minute, long second, long microsecond, Object tzInfo, long fold, - TzInfoNodes.TzInfoCheckNode tzInfoCheckNode) { + PyTZInfoCheckNode tzInfoCheckNode) { if (hour < 0 || hour >= 24) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.HOUR_MUST_BE_IN); } @@ -211,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); @@ -221,13 +226,12 @@ public static NewUnsafeNode getUncached() { @Specialization static Object newDateTime(Node inliningTarget, Object cls, int year, int month, int day, int hour, int minute, int second, int microsecond, Object tzInfoObject, int fold, @Cached PRaiseNode raiseNode, - @Cached TzInfoNodes.TzInfoCheckNode tzInfoCheckNode, + @Cached PyTZInfoCheckNode tzInfoCheckNode, @Cached TypeNodes.GetInstanceShape getInstanceShape, @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, - @Cached CExtNodes.PCallCapiFunction callCapiFunction, - @Cached ExternalFunctionNodes.DefaultCheckFunctionResultNode checkFunctionResultNode, - @Cached CApiTransitions.PythonToNativeNode toNativeNode, - @Cached CApiTransitions.NativeToPythonTransferNode fromNativeNode) { + @Cached ExternalFunctionNodes.PyObjectCheckFunctionResultNode checkFunctionResultNode, + @Cached CApiTransitions.PythonToNativeInternalNode toNativeNode, + @Cached CApiTransitions.NativeToPythonInternalNode fromNativeNode) { // create DateTime without thorough validation final Object tzInfo; @@ -246,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.execute(inliningTarget, cls); + Object effectiveTzInfo = tzInfo != null ? tzInfo : PNone.NO_VALUE; + long tzInfoPointer = toNativeNode.execute(inliningTarget, 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.executeTransfer(inliningTarget, nativeResult)); + } finally { + Reference.reachabilityFence(cls); + Reference.reachabilityFence(effectiveTzInfo); + } } } } @@ -292,86 +306,50 @@ static boolean isBuiltinClass(Object cls) { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class AsManagedDateTimeNode extends Node { - - public abstract PDateTime execute(Node inliningTarget, Object obj); - - public static PDateTime executeUncached(Object obj) { - return DateTimeNodesFactory.AsManagedDateTimeNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static PDateTime asManaged(PDateTime obj) { - return obj; - } - - @Specialization(guards = "checkNode.execute(inliningTarget, obj)", limit = "1") - static PDateTime asManagedNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject obj, - @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached DateTimeCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readByteNode, - @Cached CStructAccess.ReadObjectNode readObjectNode) { - int year = getYear(obj, readByteNode); - int month = getMonth(obj, readByteNode); - int day = getDay(obj, readByteNode); - - int hour = getHour(obj, readByteNode); - int minute = getMinute(obj, readByteNode); - int second = getSecond(obj, readByteNode); - int microsecond = getMicrosecond(obj, readByteNode); - - Object tzInfo = getTzInfo(obj, readByteNode, readObjectNode); - int fold = getFold(obj, readByteNode); - - PythonBuiltinClassType cls = PythonBuiltinClassType.PDateTime; - return new PDateTime(cls, cls.getInstanceShape(language), year, month, day, hour, minute, second, microsecond, tzInfo, fold); - } - - @Fallback - static PDateTime error(Object obj, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "datetime", obj); - } - - 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); + public static final class FromNative { + 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; } - private 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; @@ -380,8 +358,8 @@ private static Object getTzInfo(PythonAbstractNativeObject obj, CStructAccess.Re 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); } } @@ -391,6 +369,10 @@ static int getFold(PythonAbstractNativeObject self, CStructAccess.ReadByteNode r abstract static class TzInfoNode extends Node { public abstract Object execute(Node inliningTarget, Object obj); + public static Object executeUncached(Node inliningTarget, Object obj) { + return DateTimeNodesFactory.TzInfoNodeGen.getUncached().execute(inliningTarget, obj); + } + @Specialization static Object getTzInfo(PDateTime self) { return self.tzInfo; @@ -399,36 +381,16 @@ static Object getTzInfo(PDateTime self) { @Specialization static Object getTzInfo(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return AsManagedDateTimeNode.getTzInfo(self, readByteNode, readObjectNode); - } - } - - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class DateTimeCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return DateTimeNodesFactory.DateTimeCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PDateTime value) { - return true; + return FromNative.getTzInfo(self, readObjectNode); } @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PDateTime); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; + static Object getTzInfo(Node inliningTarget, Object self, + @Cached TemporalValueNodes.GetDateTimeValue readDateTimeValueNode) { + TemporalValueNodes.DateTimeValue value = readDateTimeValueNode.execute(inliningTarget, self); + return TemporalValueNodes.toPythonTzInfo(value.tzInfo, value.zoneId, inliningTarget); } } + } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java index 3af9fa1ccc..1348d28ff8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DatetimeModuleBuiltins.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 @@ -328,8 +328,8 @@ public static PTimeDelta callDst(Object tzInfo, Object dateTime, VirtualFrame fr @TruffleBoundary public static Object addOffsetToDateTime(Object dateTimeObj, PTimeDelta offset, DateTimeNodes.SubclassNewNode subclassNewNode, Node inliningTarget) { - PDateTime dateTime = DateTimeNodes.AsManagedDateTimeNode.executeUncached(dateTimeObj); - LocalDateTime utc = LocalDateTime.of(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.microsecond * 1_000).plusDays( + TemporalValueNodes.DateTimeValue dateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTimeObj); + LocalDateTime utc = dateTime.toLocalDateTime().plusDays( offset.days).plusSeconds(offset.seconds).plusNanos(offset.microseconds * 1_000L); return subclassNewNode.execute(inliningTarget, 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 new file mode 100644 index 0000000000..5cfef07b86 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java @@ -0,0 +1,420 @@ +/* + * 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.builtins.modules.datetime; + +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; + +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.lib.PyDateCheckNode; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; +import com.oracle.graal.python.lib.PyDeltaCheckNode; +import com.oracle.graal.python.lib.PyTimeCheckNode; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +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.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; +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; + +public final class TemporalValueNodes { + private TemporalValueNodes() { + } + + @ValueType + public static final class DateValue { + public final int year; + public final int month; + public final int day; + + public DateValue(int year, int month, int day) { + this.year = year; + this.month = month; + this.day = day; + } + + public static DateValue of(PDate date) { + return new DateValue(date.year, date.month, date.day); + } + + public LocalDate toLocalDate() { + return LocalDate.of(year, month, day); + } + + public int compareTo(DateValue other) { + if (year < other.year) { + return -1; + } + if (year > other.year) { + return 1; + } + if (month < other.month) { + return -1; + } + if (month > other.month) { + return 1; + } + if (day < other.day) { + return -1; + } + if (day > other.day) { + return 1; + } + return 0; + } + } + + @ValueType + public static final class TimeDeltaValue { + public final int days; + public final int seconds; + public final int microseconds; + + public TimeDeltaValue(int days, int seconds, int microseconds) { + this.days = days; + this.seconds = seconds; + this.microseconds = microseconds; + } + + public static TimeDeltaValue of(PTimeDelta delta) { + return new TimeDeltaValue(delta.days, delta.seconds, delta.microseconds); + } + + public boolean isZero() { + return days == 0 && seconds == 0 && microseconds == 0; + } + + public int compareTo(TimeDeltaValue other) { + if (days < other.days) { + return -1; + } + if (days > other.days) { + return 1; + } + if (seconds < other.seconds) { + return -1; + } + if (seconds > other.seconds) { + return 1; + } + if (microseconds < other.microseconds) { + return -1; + } + if (microseconds > other.microseconds) { + return 1; + } + return 0; + } + } + + @ValueType + public static class TimeValue { + public final int hour; + public final int minute; + public final int second; + public final int microsecond; + public final Object tzInfo; + public final ZoneId zoneId; + public final int fold; + + public TimeValue(int hour, int minute, int second, int microsecond, Object tzInfo, ZoneId zoneId, int fold) { + this.hour = hour; + this.minute = minute; + this.second = second; + this.microsecond = microsecond; + this.tzInfo = tzInfo; + this.zoneId = zoneId; + this.fold = fold; + } + + public static TimeValue of(PTime time) { + return new TimeValue(time.hour, time.minute, time.second, time.microsecond, time.tzInfo, null, time.fold); + } + + public LocalTime toLocalTime() { + return LocalTime.of(hour, minute, second, microsecond * 1_000); + } + + public boolean hasTimeZone() { + return tzInfo != null || zoneId != null; + } + } + + @ValueType + public static final class DateTimeValue extends TimeValue { + public final int year; + public final int month; + public final int day; + + public DateTimeValue(int year, int month, int day, int hour, int minute, int second, int microsecond, Object tzInfo, ZoneId zoneId, int fold) { + super(hour, minute, second, microsecond, tzInfo, zoneId, fold); + this.year = year; + this.month = month; + this.day = day; + } + + public static DateTimeValue of(PDateTime dateTime) { + return new DateTimeValue(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.microsecond, dateTime.tzInfo, null, + dateTime.fold); + } + + public DateValue getDateValue() { + return new DateValue(year, month, day); + } + + public LocalDateTime toLocalDateTime() { + return LocalDateTime.of(year, month, day, hour, minute, second, microsecond * 1_000); + } + } + + public static Object toPythonTzInfo(Object tzInfo, ZoneId zoneId, Node inliningTarget) { + if (tzInfo != null) { + return tzInfo; + } + if (zoneId == null) { + return null; + } + Object fixedOffsetTimeZone = toFixedOffsetTimeZone(zoneId, inliningTarget); + if (fixedOffsetTimeZone != null) { + return fixedOffsetTimeZone; + } + return PythonContext.get(inliningTarget).getEnv().asGuestValue(zoneId); + } + + @TruffleBoundary + public static Object toFixedOffsetTimeZone(ZoneId zoneId, Node inliningTarget) { + if (zoneId == null) { + return null; + } + final ZoneOffset offset; + if (zoneId instanceof ZoneOffset zoneOffset) { + offset = zoneOffset; + } else if (zoneId.getRules().isFixedOffset()) { + offset = zoneId.getRules().getOffset(Instant.EPOCH); + } else { + return null; + } + PTimeDelta delta = TimeDeltaNodes.NewNode.getUncached().executeBuiltin(inliningTarget, 0, offset.getTotalSeconds(), 0, 0, 0, 0, 0); + return TimeZoneNodes.NewNode.getUncached().execute(inliningTarget, PythonContext.get(inliningTarget), PythonBuiltinClassType.PTimezone, delta, PNone.NO_VALUE); + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class GetTimeDeltaValue extends Node { + public abstract TimeDeltaValue execute(Node inliningTarget, Object obj); + + public static TimeDeltaValue executeUncached(Node inliningTarget, Object obj) { + return TemporalValueNodesFactory.GetTimeDeltaValueNodeGen.getUncached().execute(inliningTarget, obj); + } + + @Specialization + static TimeDeltaValue doManaged(PTimeDelta value) { + return TimeDeltaValue.of(value); + } + + @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") + static TimeDeltaValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, + @SuppressWarnings("unused") @Cached PyDeltaCheckNode checkNode) { + return new TimeDeltaValue(TimeDeltaNodes.FromNative.getDays(value), TimeDeltaNodes.FromNative.getSeconds(value), + TimeDeltaNodes.FromNative.getMicroseconds(value)); + } + + @Fallback + static TimeDeltaValue error(Object obj, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "timedelta", obj); + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class GetDateValue extends Node { + public abstract DateValue execute(Node inliningTarget, Object obj); + + public static DateValue executeUncached(Node inliningTarget, Object obj) { + return TemporalValueNodesFactory.GetDateValueNodeGen.getUncached().execute(inliningTarget, obj); + } + + @Specialization + static DateValue doManaged(PDate value) { + return DateValue.of(value); + } + + @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") + static DateValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, + @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") + static DateValue doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + try { + LocalDate date = interop.asDate(value); + return new DateValue(date.getYear(), date.getMonthValue(), date.getDayOfMonth()); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @Fallback + static DateValue error(Object obj, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "date", obj); + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class GetTimeValue extends Node { + public abstract TimeValue execute(Node inliningTarget, Object obj); + + public static TimeValue executeUncached(Node inliningTarget, Object obj) { + return TemporalValueNodesFactory.GetTimeValueNodeGen.getUncached().execute(inliningTarget, obj); + } + + @Specialization + static TimeValue doManaged(PTime value) { + return TimeValue.of(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.ReadObjectNode readObjectNode) { + 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") + static TimeValue doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + try { + LocalTime time = interop.asTime(value); + ZoneId zoneId = interop.isTimeZone(value) ? interop.asTimeZone(value) : null; + return new TimeValue(time.getHour(), time.getMinute(), time.getSecond(), time.getNano() / 1_000, null, zoneId, 0); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @Fallback + static TimeValue error(Object obj, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "time", obj); + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class GetDateTimeValue extends Node { + public abstract DateTimeValue execute(Node inliningTarget, Object obj); + + public static DateTimeValue executeUncached(Node inliningTarget, Object obj) { + return TemporalValueNodesFactory.GetDateTimeValueNodeGen.getUncached().execute(inliningTarget, obj); + } + + @Specialization + static DateTimeValue doManaged(PDateTime value) { + return DateTimeValue.of(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.ReadObjectNode readObjectNode) { + 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") + static DateTimeValue doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + try { + LocalDate date = interop.asDate(value); + LocalTime time = interop.asTime(value); + ZoneId zoneId = interop.isTimeZone(value) ? interop.asTimeZone(value) : null; + return new DateTimeValue(date.getYear(), date.getMonthValue(), date.getDayOfMonth(), time.getHour(), time.getMinute(), time.getSecond(), time.getNano() / 1_000, null, zoneId, + 0); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @Fallback + static DateTimeValue error(Object obj, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "datetime", obj); + } + } +} 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 d306a9552c..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 @@ -68,13 +68,13 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.TimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; 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.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; @@ -87,6 +87,8 @@ 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.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; @@ -98,6 +100,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaStringNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; @@ -200,7 +203,7 @@ private static Object tryToDeserializeTime(Object cls, Object bytesObject, Objec if (naiveBytesCheck(bytes)) { // slightly different error message - if (tzInfo != PNone.NO_VALUE && !TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (tzInfo != PNone.NO_VALUE && !PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.BAD_TZINFO_STATE_ARG); } @@ -255,10 +258,12 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString repr(VirtualFrame frame, Object self, + @Bind Node inliningTarget, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return reprBoundary(self); + return reprBoundary(inliningTarget, self, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using PyObjectReprAsObjectNode) should be // connected to a current node. @@ -267,8 +272,8 @@ static TruffleString repr(VirtualFrame frame, Object self, } @TruffleBoundary - private static TruffleString reprBoundary(Object selfObj) { - PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); + private static TruffleString reprBoundary(Node inliningTarget, Object selfObj, Object tzInfo) { + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(null, selfObj); var builder = new StringBuilder(); TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj)); @@ -280,10 +285,10 @@ private static TruffleString reprBoundary(Object selfObj) { builder.append(PythonUtils.formatJString(", %d", self.second)); } - if (self.tzInfo != null) { + if (tzInfo != null) { builder.append(", tzinfo="); - Object tzinfoReprObject = PyObjectReprAsObjectNode.executeUncached(self.tzInfo); + Object tzinfoReprObject = PyObjectReprAsObjectNode.executeUncached(tzInfo); String tzinfoRepr = CastToJavaStringNode.getUncached().execute(tzinfoReprObject); builder.append(tzinfoRepr); } @@ -306,9 +311,11 @@ public abstract static class ReduceNode extends PythonUnaryBuiltinNode { static Object reduce(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TimeNodes.AsManagedTimeNode asManagedTimeNode, + @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode) { - PTime time = asManagedTimeNode.execute(inliningTarget, self); + TimeValue time = asManagedTimeNode.execute(inliningTarget, self); + Object tzInfo = tzInfoNode.execute(inliningTarget, self); // Time is serialized in the following format: // ( // bytes(hours, minutes, seconds, microseconds 1st byte, microseconds 2nd byte, @@ -327,8 +334,8 @@ static Object reduce(Object self, PBytes baseState = PFactory.createBytes(language, baseStateBytes); final PTuple arguments; - if (time.tzInfo != null) { - arguments = PFactory.createTuple(language, new Object[]{baseState, time.tzInfo}); + if (tzInfo != null) { + arguments = PFactory.createTuple(language, new Object[]{baseState, tzInfo}); } else { arguments = PFactory.createTuple(language, new Object[]{baseState}); } @@ -346,9 +353,11 @@ public abstract static class ReduceExNode extends PythonBinaryBuiltinNode { static Object reduceEx(Object self, int protocol, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TimeNodes.AsManagedTimeNode asManagedTimeNode, + @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode) { - PTime time = asManagedTimeNode.execute(inliningTarget, self); + TimeValue time = asManagedTimeNode.execute(inliningTarget, self); + Object tzInfo = tzInfoNode.execute(inliningTarget, self); byte[] baseStateBytes = new byte[6]; baseStateBytes[0] = (byte) time.hour; baseStateBytes[1] = (byte) time.minute; @@ -364,8 +373,8 @@ static Object reduceEx(Object self, int protocol, PBytes baseState = PFactory.createBytes(language, baseStateBytes); final PTuple arguments; - if (time.tzInfo != null) { - arguments = PFactory.createTuple(language, new Object[]{baseState, time.tzInfo}); + if (tzInfo != null) { + arguments = PFactory.createTuple(language, new Object[]{baseState, tzInfo}); } else { arguments = PFactory.createTuple(language, new Object[]{baseState}); } @@ -382,10 +391,11 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp op, @Bind Node inliningTarget, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return richCmpBoundary(self, other, op, inliningTarget); + return richCmpBoundary(self, other, op, inliningTarget, tzInfoNode); } finally { // A Python method call (using DatetimeModuleBuiltins.callUtcOffset) // should be connected to a current node. @@ -394,19 +404,21 @@ static Object richCmp(VirtualFrame frame, Object self, Object other, RichCmpOp o } @TruffleBoundary - private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget) { - if (!TimeNodes.TimeCheckNode.executeUncached(selfObj) || !TimeNodes.TimeCheckNode.executeUncached(otherObj)) { + private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp op, Node inliningTarget, TimeNodes.TzInfoNode tzInfoNode) { + if (!PyTimeCheckNode.executeUncached(selfObj) || !PyTimeCheckNode.executeUncached(otherObj)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); - PTime other = TimeNodes.AsManagedTimeNode.executeUncached(otherObj); + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); + TimeValue other = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, otherObj); + Object selfTzInfo = tzInfoNode.execute(inliningTarget, selfObj); + Object otherTzInfo = tzInfoNode.execute(inliningTarget, otherObj); // either naive times (without timezone) or timezones are exactly the same objects - if (self.tzInfo == other.tzInfo) { + if (selfTzInfo == otherTzInfo) { return compareTimeComponents(self, other, op); } - PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, inliningTarget); - PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(other.tzInfo, PNone.NONE, inliningTarget); + PTimeDelta selfUtcOffset = DatetimeModuleBuiltins.callUtcOffset(selfTzInfo, PNone.NONE, inliningTarget); + PTimeDelta otherUtcOffset = DatetimeModuleBuiltins.callUtcOffset(otherTzInfo, PNone.NONE, inliningTarget); if (Objects.equals(selfUtcOffset, otherUtcOffset)) { return compareTimeComponents(self, other, op); @@ -432,7 +444,7 @@ private static Object richCmpBoundary(Object selfObj, Object otherObj, RichCmpOp return op.compareResultToBool(result); } - private static boolean compareTimeComponents(PTime self, PTime other, RichCmpOp op) { + private static boolean compareTimeComponents(TimeValue self, TimeValue other, RichCmpOp op) { // compare only hours, minutes, ... and ignore fold int[] selfComponents = new int[]{self.hour, self.minute, self.second, self.microsecond}; int[] otherComponents = new int[]{other.hour, other.minute, other.second, other.microsecond}; @@ -451,12 +463,14 @@ abstract static class HashNode extends HashBuiltinNode { static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached TimeNodes.AsManagedTimeNode asManagedTimeNode, + @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, @Cached PyObjectHashNode hashNode) { - PTime self = asManagedTimeNode.execute(inliningTarget, selfObj); - PTimeDelta utcOffset = DatetimeModuleBuiltins.callUtcOffset(self.tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); + TimeValue self = asManagedTimeNode.execute(inliningTarget, selfObj); + Object tzInfo = tzInfoNode.execute(inliningTarget, selfObj); + PTimeDelta utcOffset = DatetimeModuleBuiltins.callUtcOffset(tzInfo, PNone.NONE, frame, inliningTarget, callMethodObjArgs, raiseNode); if (utcOffset == null) { var content = new int[]{self.hour, self.minute, self.second, self.microsecond}; @@ -480,9 +494,15 @@ static int getHour(PTime self) { } @Specialization - static int getHour(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getHour(self, readNode); + static int getHour(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getHour(self); + } + + @Specialization + static int getHour(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).hour; } } @@ -496,9 +516,15 @@ static int getMinute(PTime self) { } @Specialization - static int getMinute(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getMinute(self, readNode); + static int getMinute(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getMinute(self); + } + + @Specialization + static int getMinute(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).minute; } } @@ -512,9 +538,15 @@ static int getSecond(PTime self) { } @Specialization - static int getSecond(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getSecond(self, readNode); + static int getSecond(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getSecond(self); + } + + @Specialization + static int getSecond(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).second; } } @@ -528,9 +560,15 @@ static int getMicrosecond(PTime self) { } @Specialization - static int getMicrosecond(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getMicrosecond(self, readNode); + static int getMicrosecond(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getMicrosecond(self); + } + + @Specialization + static int getMicrosecond(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).microsecond; } } @@ -557,9 +595,15 @@ static int getFold(PTime self) { } @Specialization - static int getFold(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.AsManagedTimeNode.getFold(self, readNode); + static int getFold(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getFold(self); + } + + @Specialization + static int getFold(Object self, + @Bind Node inliningTarget, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + return readTimeValueNode.execute(inliningTarget, self).fold; } } @@ -946,11 +990,13 @@ public abstract static class ReplaceNode extends PythonBuiltinNode { @Specialization static Object replace(VirtualFrame frame, Object self, Object hourObject, Object minuteObject, Object secondObject, Object microsecondObject, Object tzInfoObject, Object foldObject, @Bind Node inliningTarget, - @Cached TimeNodes.AsManagedTimeNode asManagedTimeNode, + @Cached TemporalValueNodes.GetTimeValue asManagedTimeNode, @Cached PyLongAsLongNode asLongNode, + @Cached IsForeignObjectNode isForeignObjectNode, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached GetClassNode getClassNode, @Cached TimeNodes.NewNode newTimeNode) { - PTime time = asManagedTimeNode.execute(inliningTarget, self); + TimeValue time = asManagedTimeNode.execute(inliningTarget, self); final long hour, minute, second, microsecond, fold; final Object tzInfo; @@ -979,7 +1025,7 @@ static Object replace(VirtualFrame frame, Object self, Object hourObject, Object } if (tzInfoObject == PNone.NO_VALUE) { - tzInfo = time.tzInfo; + tzInfo = tzInfoNode.execute(inliningTarget, self); } else if (tzInfoObject == PNone.NONE) { tzInfo = null; } else { @@ -992,7 +1038,7 @@ static Object replace(VirtualFrame frame, Object self, Object hourObject, Object fold = asLongNode.execute(frame, inliningTarget, foldObject); } - Object type = getClassNode.execute(inliningTarget, self); + Object type = getResultTimeType(self, inliningTarget, isForeignObjectNode, getClassNode); return newTimeNode.execute(inliningTarget, type, hour, minute, second, microsecond, tzInfo, fold); } } @@ -1004,10 +1050,11 @@ public abstract static class IsoFormatNode extends PythonBinaryBuiltinNode { @Specialization static TruffleString isoFormat(VirtualFrame frame, Object self, Object timespecObject, @Bind Node inliningTarget, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return isoFormatBoundary(self, timespecObject, inliningTarget); + return isoFormatBoundary(self, timespecObject, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using PyObjectCallMethodObjArgs and // DatetimeModuleBuiltins.callUtcOffset) should be connected to a @@ -1017,8 +1064,8 @@ static TruffleString isoFormat(VirtualFrame frame, Object self, Object timespecO } @TruffleBoundary - private static TruffleString isoFormatBoundary(Object selfObj, Object timespecObject, Node inliningTarget) { - PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); + private static TruffleString isoFormatBoundary(Object selfObj, Object timespecObject, Node inliningTarget, Object tzInfo) { + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); final String timespec; @@ -1075,7 +1122,7 @@ private static TruffleString isoFormatBoundary(Object selfObj, Object timespecOb ErrorMessages.UNKNOWN_TIMESPEC_VALUE); } - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, PNone.NONE, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, PNone.NONE, true, inliningTarget); builder.append(utcOffsetString); return FromJavaStringNode.getUncached().execute(builder.toString(), TS_ENCODING); @@ -1170,10 +1217,11 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization static TruffleString strftime(VirtualFrame frame, Object self, TruffleString format, @Bind Node inliningTarget, + @Cached TimeNodes.TzInfoNode tzInfoNode, @Cached("createFor($node)") IndirectCallData.BoundaryCallData boundaryCallData) { Object saved = ExecutionContext.BoundaryCallContext.enter(frame, boundaryCallData); try { - return strftimeBoundary(self, format, inliningTarget); + return strftimeBoundary(self, format, inliningTarget, tzInfoNode.execute(inliningTarget, self)); } finally { // A Python method call (using PyObjectCallMethodObjArgs and // DatetimeModuleBuiltins.formatUtcOffset) should be connected to a @@ -1183,11 +1231,11 @@ static TruffleString strftime(VirtualFrame frame, Object self, TruffleString for } @TruffleBoundary - private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget) { - PTime self = TimeNodes.AsManagedTimeNode.executeUncached(selfObj); + private static TruffleString strftimeBoundary(Object selfObj, TruffleString format, Node inliningTarget, Object tzInfo) { + TimeValue self = TemporalValueNodes.GetTimeValue.executeUncached(inliningTarget, selfObj); // Reuse time.strftime(format, time_tuple) method. int[] timeTuple = new int[]{1900, 1, 1, self.hour, self.minute, self.second, 0, 1, -1}; - String formatPreprocessed = preprocessFormat(format, self, inliningTarget); + String formatPreprocessed = preprocessFormat(format, self, inliningTarget, tzInfo); return TimeModuleBuiltins.StrfTimeNode.format(formatPreprocessed, timeTuple, TruffleString.FromJavaStringNode.getUncached()); } @@ -1195,7 +1243,7 @@ private static TruffleString strftimeBoundary(Object selfObj, TruffleString form // %Z so handle them here. // CPython: wrap_strftime() @TruffleBoundary - private static String preprocessFormat(TruffleString tsformat, PTime self, Node inliningTarget) { + private static String preprocessFormat(TruffleString tsformat, TimeValue self, Node inliningTarget, Object tzInfo) { String format = tsformat.toString(); StringBuilder builder = new StringBuilder(); int i = 0; @@ -1218,13 +1266,13 @@ private static String preprocessFormat(TruffleString tsformat, PTime self, Node char c = format.charAt(p + 1); if (c == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, PNone.NONE, false, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, PNone.NONE, false, inliningTarget); builder.append(utcOffsetString); i = p + 2; } else if (c == 'Z') { - if (self.tzInfo != null) { + if (tzInfo != null) { // call tzname() - Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(self.tzInfo, T_TZNAME, PNone.NONE); + Object tzNameObject = PyObjectCallMethodObjArgs.executeUncached(tzInfo, T_TZNAME, PNone.NONE); // ignore None value if (tzNameObject != PNone.NONE) { @@ -1255,7 +1303,7 @@ private static String preprocessFormat(TruffleString tsformat, PTime self, Node char d = format.charAt(p + 2); if (d == 'z') { - Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(self.tzInfo, PNone.NONE, true, inliningTarget); + Object utcOffsetString = DatetimeModuleBuiltins.formatUtcOffset(tzInfo, PNone.NONE, true, inliningTarget); builder.append(utcOffsetString); i = p + 3; @@ -1295,7 +1343,7 @@ static Object format(VirtualFrame frame, Object self, TruffleString format, } } - private static long toMicroseconds(PTime self, PTimeDelta utcOffset) { + private static long toMicroseconds(TimeValue self, PTimeDelta utcOffset) { return (long) self.hour * 3600 * 1_000_000 + (long) self.minute * 60 * 1_000_000 + (long) self.second * 1_000_000 + @@ -1304,4 +1352,8 @@ private static long toMicroseconds(PTime self, PTimeDelta utcOffset) { (long) utcOffset.seconds * 1_000_000 - (long) utcOffset.microseconds; } + + private static Object getResultTimeType(Object self, Node inliningTarget, IsForeignObjectNode isForeignObjectNode, GetClassNode getClassNode) { + return isForeignObjectNode.execute(inliningTarget, self) ? PythonBuiltinClassType.PTime : getClassNode.execute(inliningTarget, self); + } } 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 88cfccc818..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,6 +76,7 @@ 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.lib.PyDeltaCheckNode; import com.oracle.graal.python.lib.PyFloatCheckNode; import com.oracle.graal.python.lib.PyLongCheckNode; import com.oracle.graal.python.lib.PyNumberAddNode; @@ -163,8 +164,8 @@ abstract static class BoolNode extends TpSlotInquiry.NbBoolBuiltinNode { @Specialization static boolean bool(Object selfObj, @Bind Node inliningTarget, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return self.days != 0 || self.seconds != 0 || self.microseconds != 0; } } @@ -176,10 +177,10 @@ public abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static TruffleString repr(Object selfObj) { - PTimeDelta self = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(selfObj); + TimeDeltaValue self = TemporalValueNodes.GetTimeDeltaValue.executeUncached(null, selfObj); var builder = new StringBuilder(); - builder.append(TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self))); + builder.append(TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(selfObj))); builder.append("("); @@ -223,8 +224,9 @@ public abstract static class StrNode extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary - static TruffleString str(Object selfObj) { - PTimeDelta self = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(selfObj); + static TruffleString str(Object selfObj, + @Bind Node inliningTarget) { + TimeDeltaValue self = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, selfObj); var builder = new StringBuilder(); // optional prefix with days, e.g. '1 day' or '5 days' @@ -269,8 +271,8 @@ static Object reduce(Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached GetClassNode getClassNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); Object type = getClassNode.execute(inliningTarget, selfObj); PTuple arguments = PFactory.createTuple(language, new Object[]{self.days, self.seconds, self.microseconds}); return PFactory.createTuple(language, new Object[]{type, arguments}); @@ -284,13 +286,13 @@ abstract static class RichCmpNode extends RichCmpBuiltinNode { @Specialization static Object richCmp(Object left, Object right, RichCmpOp op, @Bind Node inliningTarget, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached PyDeltaCheckNode checkNode, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); - PTimeDelta other = asManagedTimeDeltaNode.execute(inliningTarget, right); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); + TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, right); int result = self.compareTo(other); return op.compareResultToBool(result); } @@ -305,8 +307,8 @@ static long hash(VirtualFrame frame, Object selfObj, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectHashNode hashNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); var content = new int[]{self.days, self.seconds, self.microseconds}; return hashNode.execute(frame, inliningTarget, PFactory.createTuple(language, content)); } @@ -321,13 +323,13 @@ abstract static class AddNode extends BinaryOpBuiltinNode { static Object add(Object left, Object right, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached PyDeltaCheckNode checkNode, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, right)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); - PTimeDelta other = asManagedTimeDeltaNode.execute(inliningTarget, right); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); + TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, right); return newNode.executeBuiltin(inliningTarget, self.days + other.days, self.seconds + other.seconds, self.microseconds + other.microseconds, 0, 0, 0, 0); } } @@ -341,13 +343,13 @@ abstract static class SubNode extends BinaryOpBuiltinNode { static Object sub(Object left, Object rigth, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached PyDeltaCheckNode checkNode, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkNode.execute(inliningTarget, left) || !checkNode.execute(inliningTarget, rigth)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); - PTimeDelta other = asManagedTimeDeltaNode.execute(inliningTarget, rigth); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); + TimeDeltaValue other = readTimeDeltaValueNode.execute(inliningTarget, rigth); return newNode.executeBuiltin(inliningTarget, self.days - other.days, self.seconds - other.seconds, self.microseconds - other.microseconds, 0, 0, 0, 0); } } @@ -413,15 +415,15 @@ static Object mul(VirtualFrame frame, Object left, Object right, @Cached PyNumberAddNode addNode, @Cached PyNumberMultiplyNode multiplyNode, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta date; + @Cached PyDeltaCheckNode checkNode, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + TimeDeltaValue date; Object other; if (checkNode.execute(inliningTarget, left)) { - date = asManagedTimeDeltaNode.execute(inliningTarget, left); + date = readTimeDeltaValueNode.execute(inliningTarget, left); other = right; } else { - date = asManagedTimeDeltaNode.execute(inliningTarget, right); + date = readTimeDeltaValueNode.execute(inliningTarget, right); other = left; } if (longCheckNode.execute(inliningTarget, other)) { @@ -465,15 +467,15 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached PyNumberMultiplyNode multiplyNode, @Cached PyNumberTrueDivideNode trueDivideNode, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkLeft, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkRight, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached PyDeltaCheckNode checkLeft, + @Cached PyDeltaCheckNode checkRight, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); if (checkRight.execute(inliningTarget, right)) { - PTimeDelta otherTimeDelta = asManagedTimeDeltaNode.execute(inliningTarget, right); + TimeDeltaValue otherTimeDelta = readTimeDeltaValueNode.execute(inliningTarget, right); Object microsecondsSelf = toMicroseconds(self, addNode, multiplyNode); Object microsecondsOther = toMicroseconds(otherTimeDelta, addNode, multiplyNode); return trueDivideNode.execute(frame, microsecondsSelf, microsecondsOther); @@ -513,15 +515,15 @@ static Object div(VirtualFrame frame, Object left, Object right, @Cached PyNumberAddNode addNode, @Cached PyNumberMultiplyNode multiplyNode, @Cached PyNumberFloorDivideNode floorDivideNode, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkLeft, - @Cached TimeDeltaNodes.TimeDeltaCheckNode checkRight, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { + @Cached PyDeltaCheckNode checkLeft, + @Cached PyDeltaCheckNode checkRight, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { if (!checkLeft.execute(inliningTarget, left)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, left); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, left); if (checkRight.execute(inliningTarget, right)) { - PTimeDelta otherTimeDelta = asManagedTimeDeltaNode.execute(inliningTarget, right); + TimeDeltaValue otherTimeDelta = readTimeDeltaValueNode.execute(inliningTarget, right); Object microsecondsSelf = toMicroseconds(self, addNode, multiplyNode); Object microsecondsOther = toMicroseconds(otherTimeDelta, addNode, multiplyNode); return floorDivideNode.execute(frame, microsecondsSelf, microsecondsOther); @@ -544,11 +546,11 @@ abstract static class DivModNode extends BinaryOpBuiltinNode { static Object divmod(Object left, Object right, @Bind Node inliningTarget, @Bind PythonLanguage language) { - if (!TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) || !TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (!PyDeltaCheckNode.executeUncached(left) || !PyDeltaCheckNode.executeUncached(right)) { return PNotImplemented.NOT_IMPLEMENTED; } - PTimeDelta self = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(left); - PTimeDelta other = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + TimeDeltaValue self = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, left); + TimeDeltaValue other = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); @@ -576,14 +578,14 @@ abstract static class ModNode extends BinaryOpBuiltinNode { @TruffleBoundary static Object mod(Object left, Object right, @Bind Node inliningTarget) { - if (!TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(left) || !TimeDeltaNodes.TimeDeltaCheckNode.executeUncached(right)) { + if (!PyDeltaCheckNode.executeUncached(left) || !PyDeltaCheckNode.executeUncached(right)) { return PNotImplemented.NOT_IMPLEMENTED; } EncapsulatingNodeReference encapsulating = EncapsulatingNodeReference.getCurrent(); Node encapsulatingNode = encapsulating.set(inliningTarget); try { - PTimeDelta self = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(left); - PTimeDelta other = TimeDeltaNodes.AsManagedTimeDeltaNode.executeUncached(right); + TimeDeltaValue self = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, left); + TimeDeltaValue other = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, right); Object microsecondsSelf = toMicrosecondsUncached(self); Object microsecondsOther = toMicrosecondsUncached(other); Object remainder = PyNumberRemainderNode.getUncached().execute(null, microsecondsSelf, microsecondsOther); @@ -603,8 +605,8 @@ abstract static class AbsNode extends PythonUnaryBuiltinNode { static PTimeDelta abs(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); if (self.days >= 0) { return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); } else { @@ -621,8 +623,8 @@ abstract static class PosNode extends PythonUnaryBuiltinNode { static PTimeDelta pos(PTimeDelta selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, self.days, self.seconds, self.microseconds, 0, 0, 0, 0); } } @@ -635,8 +637,8 @@ abstract static class NegNode extends PythonUnaryBuiltinNode { static PTimeDelta neg(Object selfObj, @Bind Node inliningTarget, @Cached TimeDeltaNodes.NewNode newNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode) { + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); return newNode.executeBuiltin(inliningTarget, -self.days, -self.seconds, -self.microseconds, 0, 0, 0, 0); } } @@ -651,9 +653,8 @@ static int getDays(PTimeDelta self) { } @Specialization - static int getDays(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.AsManagedTimeDeltaNode.getDays(self, readNode); + static int getDays(PythonAbstractNativeObject self) { + return TimeDeltaNodes.FromNative.getDays(self); } } @@ -667,9 +668,8 @@ static int getSeconds(PTimeDelta self) { } @Specialization - static int getSeconds(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.AsManagedTimeDeltaNode.getSeconds(self, readNode); + static int getSeconds(PythonAbstractNativeObject self) { + return TimeDeltaNodes.FromNative.getSeconds(self); } } @@ -683,9 +683,8 @@ static int getMicroseconds(PTimeDelta self) { } @Specialization - static int getMicroseconds(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.AsManagedTimeDeltaNode.getMicroseconds(self, readNode); + static int getMicroseconds(PythonAbstractNativeObject self) { + return TimeDeltaNodes.FromNative.getMicroseconds(self); } } @@ -696,24 +695,24 @@ abstract static class TotalSecondsNode extends PythonUnaryBuiltinNode { @Specialization static Object getTotalSeconds(Object selfObj, @Bind Node inliningTarget, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode, + @Cached TemporalValueNodes.GetTimeDeltaValue readTimeDeltaValueNode, @Cached PyNumberAddNode addNode, @Cached PyNumberMultiplyNode multiplyNode, @Cached PyNumberTrueDivideNode trueDivideNode) { - PTimeDelta self = asManagedTimeDeltaNode.execute(inliningTarget, selfObj); + TimeDeltaValue self = readTimeDeltaValueNode.execute(inliningTarget, selfObj); Object microseconds = toMicroseconds(self, addNode, multiplyNode); return trueDivideNode.execute(null, microseconds, 1_000_000); } } - private static Object toMicroseconds(PTimeDelta timeDelta, PyNumberAddNode addNode, PyNumberMultiplyNode multiplyNode) { + private static Object toMicroseconds(TimeDeltaValue timeDelta, PyNumberAddNode addNode, PyNumberMultiplyNode multiplyNode) { Object x = multiplyNode.execute(null, timeDelta.days, 24 * 3600); x = addNode.execute(null, x, timeDelta.seconds); x = multiplyNode.execute(null, x, 1_000_000); return addNode.execute(null, x, timeDelta.microseconds); } - private static Object toMicrosecondsUncached(PTimeDelta timeDelta) { + private static Object toMicrosecondsUncached(TimeDeltaValue timeDelta) { return toMicroseconds(timeDelta, PyNumberAddNode.getUncached(), PyNumberMultiplyNode.getUncached()); } 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 6dcd2390f9..dd9aaee63a 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,19 +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.PythonLanguage; 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.NativeToPythonInternalNode; 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,13 +67,11 @@ import com.oracle.graal.python.lib.PyNumberMultiplyNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; 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.Bind; 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; @@ -139,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); @@ -176,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.PythonToNativeInternalNode.executeUncached(cls, false); + 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(), NativeToPythonInternalNode.executeUncached(nativeResult, true)); + } finally { + Reference.reachabilityFence(cls); + } } } @@ -239,77 +249,17 @@ public BigInteger getTotalMicroseconds() { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class TimeDeltaCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return TimeDeltaNodesFactory.TimeDeltaCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PTimeDelta value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTimeDelta); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } - } - - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class AsManagedTimeDeltaNode extends Node { - public abstract PTimeDelta execute(Node inliningTarget, Object obj); - - public static PTimeDelta executeUncached(Object obj) { - return TimeDeltaNodesFactory.AsManagedTimeDeltaNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static PTimeDelta doPTimeDelta(PTimeDelta value) { - return value; - } - - @Specialization(guards = "checkNode.execute(inliningTarget, nativeDelta)", limit = "1") - static PTimeDelta doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject nativeDelta, - @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached TimeDeltaCheckNode checkNode, - @Cached CStructAccess.ReadI32Node readIntNode) { - int days = getDays(nativeDelta, readIntNode); - int seconds = getSeconds(nativeDelta, readIntNode); - int microseconds = getMicroseconds(nativeDelta, readIntNode); - - PythonBuiltinClassType cls = PythonBuiltinClassType.PTimeDelta; - return new PTimeDelta(cls, cls.getInstanceShape(language), days, seconds, microseconds); - } - - static int getDays(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { - return readNode.readFromObj(self, CFields.PyDateTime_Delta__days); - } - - static int getSeconds(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { - return readNode.readFromObj(self, CFields.PyDateTime_Delta__seconds); + public static final class FromNative { + static int getDays(PythonAbstractNativeObject self) { + return readIntField(self.getPtr(), CFields.PyDateTime_Delta__days); } - static int getMicroseconds(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { - return readNode.readFromObj(self, CFields.PyDateTime_Delta__microseconds); + static int getSeconds(PythonAbstractNativeObject self) { + return readIntField(self.getPtr(), CFields.PyDateTime_Delta__seconds); } - @Fallback - static PTimeDelta error(Object obj, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "timedelta", obj); + 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 e68f2781e5..59ebbe8753 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,30 +42,36 @@ 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 com.oracle.graal.python.PythonLanguage; +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.NativeToPythonInternalNode; 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.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.nodes.object.BuiltinClassProfiles; +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.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; @@ -146,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; @@ -159,11 +167,20 @@ public static Object newTimeUnchecked(Object cls, int hour, int minute, int seco Shape shape = GetInstanceShape.executeUncached(cls); 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 = CApiTransitions.PythonToNativeInternalNode.executeUncached(cls, false); + Object effectiveTzInfo = tzInfo != null ? tzInfo : PNone.NO_VALUE; + long tzInfoPointer = CApiTransitions.PythonToNativeInternalNode.executeUncached(effectiveTzInfo, false); + 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(), NativeToPythonInternalNode.executeUncached(nativeResult, true)); + } finally { + Reference.reachabilityFence(cls); + Reference.reachabilityFence(effectiveTzInfo); + } } } @@ -185,7 +202,7 @@ private static void validateTimeComponents(Node inliningTarget, long hour, long throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ErrorMessages.MICROSECOND_MUST_BE_IN); } - if (tzInfo != null && !TzInfoNodes.TzInfoCheckNode.executeUncached(tzInfo)) { + if (tzInfo != null && !PyTZInfoCheckNode.executeUncached(tzInfo)) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.TZINFO_ARGUMENT_MUST_BE_NONE_OR_OF_A_TZINFO_SUBCLASS_NOT_TYPE_P, tzInfo); } @@ -230,61 +247,33 @@ static boolean isBuiltinClass(Object cls) { } } - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class AsManagedTimeNode extends Node { - public abstract PTime execute(Node inliningTarget, Object obj); - - public static PTime executeUncached(Object obj) { - return TimeNodesFactory.AsManagedTimeNodeGen.getUncached().execute(null, obj); + public static final class FromNative { + static int getHour(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Time__data); + return NativeMemory.readByteArrayElement(ptr, 0) & 0xFF; } - @Specialization - static PTime doPTime(PTime value) { - return value; + static int getMinute(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Time__data); + return NativeMemory.readByteArrayElement(ptr, 1) & 0xFF; } - @Specialization(guards = "checkNode.execute(inliningTarget, nativeTime)", limit = "1") - static PTime doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject nativeTime, - @Bind PythonLanguage language, - @SuppressWarnings("unused") @Cached TimeCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readByteNode, - @Cached CStructAccess.ReadObjectNode readObjectNode) { - int hour = getHour(nativeTime, readByteNode); - int minute = getMinute(nativeTime, readByteNode); - int second = getSecond(nativeTime, readByteNode); - int microsecond = getMicrosecond(nativeTime, readByteNode); - - Object tzinfo = getTzInfo(nativeTime, readByteNode, readObjectNode); - int fold = getFold(nativeTime, readByteNode); - - PythonBuiltinClassType cls = PythonBuiltinClassType.PTime; - return new PTime(cls, cls.getInstanceShape(language), hour, minute, second, microsecond, tzinfo, fold); + static int getSecond(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Time__data); + return NativeMemory.readByteArrayElement(ptr, 2) & 0xFF; } - static int getHour(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 0); - } - - static int getMinute(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 1); - } - - static int getSecond(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 2); - } - - 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; } - private 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; @@ -293,41 +282,8 @@ private static Object getTzInfo(PythonAbstractNativeObject nativeTime, CStructAc return tzinfo; } - static int getFold(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__fold); - } - - @Fallback - static PTime error(Object obj, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_EXPECTED_GOT_P, "time", obj); - } - } - - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class TimeCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); - - public static boolean executeUncached(Object obj) { - return TimeNodesFactory.TimeCheckNodeGen.getUncached().execute(null, obj); - } - - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PTime value) { - return true; - } - - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTime); - } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; + static int getFold(PythonAbstractNativeObject self) { + return readByteField(self.getPtr(), CFields.PyDateTime_Time__fold) & 0xFF; } } @@ -337,6 +293,10 @@ static boolean doOther(@SuppressWarnings("unused") Object value) { abstract static class TzInfoNode extends Node { public abstract Object execute(Node inliningTarget, Object obj); + public static Object executeUncached(Node inliningTarget, Object obj) { + return TimeNodesFactory.TzInfoNodeGen.getUncached().execute(inliningTarget, obj); + } + @Specialization static Object getTzInfo(PTime self) { return self.tzInfo; @@ -345,9 +305,15 @@ static Object getTzInfo(PTime self) { @Specialization static Object getTzInfo(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return AsManagedTimeNode.getTzInfo(self, readByteNode, readObjectNode); + return FromNative.getTzInfo(self, readObjectNode); + } + + @Specialization + static Object getTzInfo(Node inliningTarget, Object self, + @Cached TemporalValueNodes.GetTimeValue readTimeValueNode) { + TemporalValueNodes.TimeValue timeValue = readTimeValueNode.execute(inliningTarget, self); + return TemporalValueNodes.toPythonTzInfo(timeValue.tzInfo, timeValue.zoneId, inliningTarget); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java index d0ff0aa76d..835c58cccb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneBuiltins.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 @@ -71,6 +71,7 @@ import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; @@ -247,25 +248,18 @@ static long hash(VirtualFrame frame, PTimeZone self, @GenerateNodeFactory public abstract static class UtcOffsetNode extends PythonBinaryBuiltinNode { - @Specialization - static PTimeDelta utcOffset(PTimeZone self, PDateTime dt) { - return self.offset; - } - @Specialization(guards = {"isNone(dt)"}) static PTimeDelta utcOffsetForNone(PTimeZone self, PNone dt) { return self.offset; } - @Fallback - static void doGeneric(Object self, Object dt, + @Specialization(guards = {"!isNone(dt)"}) + static PTimeDelta utcOffset(PTimeZone self, Object dt, @Bind Node inliningTarget, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached PRaiseNode raiseNode) { - throw raiseNode.raise(inliningTarget, TypeError, - ErrorMessages.S_ARGUMENT_MUST_BE_A_S_INSTANCE_OR_NONE_NOT_P, - "utcoffset(dt)", - "datetime", - dt); + validateDateTimeOrNone(dt, "utcoffset(dt)", inliningTarget, dateTimeCheckNode, raiseNode); + return self.offset; } } @@ -273,26 +267,18 @@ static void doGeneric(Object self, Object dt, @GenerateNodeFactory public abstract static class DstNode extends PythonBinaryBuiltinNode { - @Specialization - static Object dst(PTimeZone self, PDateTime dt) { - return PNone.NONE; - } - @Specialization(guards = {"isNone(dt)"}) static Object dst(PTimeZone self, PNone dt) { return PNone.NONE; } - @Fallback - static void doGeneric(Object self, Object dt, + @Specialization(guards = {"!isNone(dt)"}) + static Object dst(PTimeZone self, Object dt, @Bind Node inliningTarget, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached PRaiseNode raiseNode) { - throw raiseNode.raise(inliningTarget, - TypeError, - ErrorMessages.S_ARGUMENT_MUST_BE_A_S_INSTANCE_OR_NONE_NOT_P, - "dst(dt)", - "datetime", - dt); + validateDateTimeOrNone(dt, "dst(dt)", inliningTarget, dateTimeCheckNode, raiseNode); + return PNone.NONE; } } @@ -300,26 +286,18 @@ static void doGeneric(Object self, Object dt, @GenerateNodeFactory public abstract static class TzNameNode extends PythonBinaryBuiltinNode { - @Specialization - static TruffleString tzName(PTimeZone self, PDateTime dt) { - return getTzName(self); - } - @Specialization(guards = {"isNone(dt)"}) static TruffleString tzName(PTimeZone self, PNone dt) { return getTzName(self); } - @Fallback - static void doGeneric(Object self, Object dt, + @Specialization(guards = {"!isNone(dt)"}) + static TruffleString tzName(PTimeZone self, Object dt, @Bind Node inliningTarget, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached PRaiseNode raiseNode) { - throw raiseNode.raise(inliningTarget, - TypeError, - ErrorMessages.S_ARGUMENT_MUST_BE_A_S_INSTANCE_OR_NONE_NOT_P, - "tzname(dt)", - "datetime", - dt); + validateDateTimeOrNone(dt, "tzname(dt)", inliningTarget, dateTimeCheckNode, raiseNode); + return getTzName(self); } } @@ -350,7 +328,7 @@ public abstract static class FromUtcNode extends PythonBinaryBuiltinNode { @Specialization static Object fromUtc(PTimeZone self, Object dateTime, @Bind Node inliningTarget, - @Cached DateTimeNodes.DateTimeCheckNode dateTimeCheckNode, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached PRaiseNode raiseNode, @Cached DateTimeNodes.SubclassNewNode dateTimeSubclassNewNode) { @@ -400,4 +378,15 @@ static TruffleString getTzName(PTimeZone timezone) { return TruffleString.FromJavaStringNode.getUncached().execute(builder.toString(), TS_ENCODING); } + + private static void validateDateTimeOrNone(Object dt, String methodName, Node inliningTarget, PyDateTimeCheckNode dateTimeCheckNode, PRaiseNode raiseNode) { + if (!dateTimeCheckNode.execute(inliningTarget, dt)) { + throw raiseNode.raise(inliningTarget, + TypeError, + ErrorMessages.S_ARGUMENT_MUST_BE_A_S_INSTANCE_OR_NONE_NOT_P, + methodName, + "datetime", + dt); + } + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java index 792e4de29b..dd9c59d40f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeZoneNodes.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 @@ -42,13 +42,18 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.PyDeltaCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; 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.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; @@ -72,8 +77,8 @@ public static NewNode getUncached() { @Specialization static PTimeZone newTimezone(Node inliningTarget, PythonContext context, Object cls, Object offsetObj, Object nameObject, - @Cached TimeDeltaNodes.TimeDeltaCheckNode timeDeltaCheckNode, - @Cached TimeDeltaNodes.AsManagedTimeDeltaNode asManagedTimeDeltaNode, + @Bind PythonLanguage language, + @Cached PyDeltaCheckNode timeDeltaCheckNode, @Cached CastToTruffleStringNode castToTruffleStringNode, @Cached PRaiseNode raiseNode, @Cached TypeNodes.GetInstanceShape getInstanceShape) { @@ -86,7 +91,14 @@ static PTimeZone newTimezone(Node inliningTarget, PythonContext context, Object "datetime.timedelta", offsetObj); } - PTimeDelta offset = asManagedTimeDeltaNode.execute(inliningTarget, offsetObj); + PTimeDelta offset; + if (offsetObj instanceof PTimeDelta value) { + offset = value; + } else { + TimeDeltaValue offsetValue = TemporalValueNodes.GetTimeDeltaValue.executeUncached(inliningTarget, offsetObj); + PythonBuiltinClassType tdcls = PythonBuiltinClassType.PTimeDelta; + offset = new PTimeDelta(tdcls, tdcls.getInstanceShape(language), offsetValue.days, offsetValue.seconds, offsetValue.microseconds); + } final TruffleString name; if (nameObject == PNone.NO_VALUE) { name = null; 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 08ebb60de0..6e22c65e13 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,14 +60,18 @@ 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.NativeToPythonInternalNode; 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; import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetInstanceShape; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetStateNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; @@ -77,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; @@ -101,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, @@ -110,11 +118,17 @@ 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 = CApiTransitions.PythonToNativeInternalNode.executeUncached(cls, false); + 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(), NativeToPythonInternalNode.executeUncached(nativeResult, true)); + } finally { + Reference.reachabilityFence(cls); + } } } } @@ -174,7 +188,7 @@ public abstract static class FromUtcNode extends PythonBinaryBuiltinNode { @Specialization static Object fromUtc(VirtualFrame frame, Object self, Object dateTime, @Bind Node inliningTarget, - @Cached DateTimeNodes.DateTimeCheckNode dateTimeCheckNode, + @Cached PyDateTimeCheckNode dateTimeCheckNode, @Cached DateTimeNodes.TzInfoNode tzInfoNode, @Cached PyObjectCallMethodObjArgs callMethodObjArgs, @Cached PRaiseNode raiseNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/HashlibModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/HashlibModuleBuiltins.java index 5817b3427f..8c4e666d65 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/HashlibModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/HashlibModuleBuiltins.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 @@ -56,6 +56,8 @@ import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -67,12 +69,6 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.jcajce.provider.util.DigestFactory; - import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; import com.oracle.graal.python.annotations.Builtin; @@ -88,7 +84,6 @@ import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes; import com.oracle.graal.python.builtins.objects.module.PythonModule; -import com.oracle.graal.python.builtins.objects.ssl.LazyBouncyCastleProvider; import com.oracle.graal.python.lib.PyLongAsLongNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; @@ -104,8 +99,8 @@ import com.oracle.graal.python.nodes.util.CastToJavaStringNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData; +import com.oracle.graal.python.runtime.crypto.BouncyCastleSupportProvider; 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; @@ -172,7 +167,6 @@ public void postInitialize(Python3Core core) { PythonLanguage language = core.getLanguage(); PythonModule self = core.lookupBuiltinModule(T_HASHLIB); EconomicMapStorage storage = EconomicMapStorage.create(); - LazyBouncyCastleProvider.initProvider(); ArrayList digests = new ArrayList<>(); for (var provider : Security.getProviders()) { for (var service : provider.getServices()) { @@ -399,12 +393,29 @@ static Object doIt(VirtualFrame frame, Node inliningTarget, PythonBuiltinClassTy @TruffleBoundary private static MessageDigest createDigest(String name, byte[] bytes, int bytesLen) throws NoSuchAlgorithmException { - MessageDigest digest = MessageDigest.getInstance(name); + MessageDigest digest; + try { + digest = MessageDigest.getInstance(name); + } catch (NoSuchAlgorithmException primary) { + if (!isBouncyCastleDigest(name)) { + throw primary; + } + try { + digest = BouncyCastleSupportProvider.createDigest(name); + } catch (NoSuchAlgorithmException secondary) { + primary.addSuppressed(secondary); + throw primary; + } + } if (bytes != null) { digest.update(bytes, 0, bytesLen); } return digest; } + + private static boolean isBouncyCastleDigest(String name) { + return name.startsWith("BLAKE2B-") || name.startsWith("BLAKE2S-") || name.equals("SHAKE128") || name.equals("SHAKE256"); + } } @Builtin(name = "new", minNumOfPositionalArgs = 1, parameterNames = {"name", "string"}, keywordOnlyNames = {"usedforsecurity"}) @@ -481,10 +492,7 @@ static Object pbkdf2(VirtualFrame frame, TruffleString hashName, Object password @Cached TruffleString.ToJavaStringNode toJavaStringNode, @Cached PRaiseNode raiseNode) { try { - Digest digest = getDigest(toJavaStringNode.execute(hashName)); - if (digest == null) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.UnsupportedDigestmodError, UNSUPPORTED_HASH_TYPE, hashName); - } + String javaHashName = toJavaStringNode.execute(hashName); if (iterations < 1) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.ValueError, ITERATION_VALUE_MUST_BE_GREATER_THAN_ZERO); } @@ -493,11 +501,10 @@ static Object pbkdf2(VirtualFrame frame, TruffleString hashName, Object password } long dklen; if (noDklenProfile.profile(inliningTarget, PGuards.isPNone(dklenObj))) { - dklen = digest.getDigestSize(); + dklen = getPbkdf2MacLength(javaHashName); } else { dklen = asLongNode.execute(frame, inliningTarget, dklenObj); } - dklen *= Byte.SIZE; if (dklen < 1) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.ValueError, KEY_LENGTH_MUST_BE_GREATER_THAN_ZERO); } @@ -506,7 +513,9 @@ static Object pbkdf2(VirtualFrame frame, TruffleString hashName, Object password } byte[] passwordBytes = passwordLib.getInternalOrCopiedExactByteArray(password); byte[] saltBytes = saltLib.getInternalOrCopiedExactByteArray(salt); - return PFactory.createBytes(language, generate(digest, passwordBytes, saltBytes, (int) iterations, (int) dklen)); + return PFactory.createBytes(language, generate(javaHashName, passwordBytes, saltBytes, (int) iterations, (int) dklen)); + } catch (GeneralSecurityException e) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.UnsupportedDigestmodError, UNSUPPORTED_HASH_TYPE, hashName); } finally { passwordLib.release(password); saltLib.release(salt); @@ -514,20 +523,70 @@ static Object pbkdf2(VirtualFrame frame, TruffleString hashName, Object password } @TruffleBoundary - private static Digest getDigest(String name) { - name = name.toLowerCase(); - return DigestFactory.getDigest(NAME_MAPPINGS.getOrDefault(name, name)); + private static int getPbkdf2MacLength(String name) throws NoSuchAlgorithmException { + return switch (name.toLowerCase()) { + case "sha1" -> 20; + case "sha224" -> 28; + case "sha256" -> 32; + case "sha384" -> 48; + case "sha512" -> 64; + default -> throw new NoSuchAlgorithmException(name); + }; } @TruffleBoundary - private static byte[] generate(Digest digest, byte[] password, byte[] salt, int iterations, int dklen) { - PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator(digest); - generator.init(password, salt, iterations); - CipherParameters cipherParameters = generator.generateDerivedParameters(dklen); - if (!(cipherParameters instanceof KeyParameter keyParameter)) { - throw CompilerDirectives.shouldNotReachHere("unexpected cipher parameters"); + private static Mac createPbkdf2Mac(String name) throws NoSuchAlgorithmException { + String algorithm = getPbkdf2Algorithm(name); + if (algorithm == null) { + throw new NoSuchAlgorithmException(name); + } + return Mac.getInstance(algorithm); + } + + private static String getPbkdf2Algorithm(String name) { + return switch (name.toLowerCase()) { + case "sha1" -> "HmacSHA1"; + case "sha224" -> "HmacSHA224"; + case "sha256" -> "HmacSHA256"; + case "sha384" -> "HmacSHA384"; + case "sha512" -> "HmacSHA512"; + default -> null; + }; + } + + @TruffleBoundary + private static byte[] generate(String hashName, byte[] password, byte[] salt, int iterations, int dklen) throws GeneralSecurityException { + Mac mac = createPbkdf2Mac(hashName); + try { + String algorithm = mac.getAlgorithm(); + mac.init(new SecretKeySpec(password, algorithm)); + int digestLength = mac.getMacLength(); + byte[] derivedKey = new byte[dklen]; + byte[] block = new byte[digestLength]; + byte[] u = new byte[digestLength]; + byte[] blockIndex = new byte[Integer.BYTES]; + int blockCount = (dklen + digestLength - 1) / digestLength; + for (int i = 1; i <= blockCount; i++) { + ByteBuffer.wrap(blockIndex).putInt(i); + mac.update(salt); + mac.update(blockIndex); + byte[] initial = mac.doFinal(); + System.arraycopy(initial, 0, block, 0, digestLength); + System.arraycopy(initial, 0, u, 0, digestLength); + for (int j = 1; j < iterations; j++) { + u = mac.doFinal(u); + for (int k = 0; k < digestLength; k++) { + block[k] ^= u[k]; + } + } + int offset = (i - 1) * digestLength; + int length = Math.min(digestLength, dklen - offset); + System.arraycopy(block, 0, derivedKey, offset, length); + } + return derivedKey; + } catch (InvalidKeyException e) { + throw new IllegalStateException(e); } - return keyParameter.getKey(); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedIOMixinBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedIOMixinBuiltins.java index 5d8a1f3cbc..eb89c28eb9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedIOMixinBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedIOMixinBuiltins.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 @@ -275,7 +275,7 @@ static long doit(VirtualFrame frame, PBuffered self, Object off, int whence, @Cached("create(T_SEEK)") CheckIsClosedNode checkIsClosedNode, @Cached BufferedIONodes.CheckIsSeekabledNode checkIsSeekabledNode, @Cached BufferedIONodes.AsOffNumberNode asOffNumberNode, - @Cached(inline = true) BufferedIONodes.SeekNode seekNode) { + @Cached BufferedIONodes.SeekNode seekNode) { checkIsClosedNode.execute(frame, self); checkIsSeekabledNode.execute(frame, self); long pos = asOffNumberNode.execute(frame, inliningTarget, off, TypeError); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedIONodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedIONodes.java index cd33e41480..a1835cbe60 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedIONodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedIONodes.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 @@ -131,12 +131,12 @@ static boolean isClosed(PBuffered self) { } @SuppressWarnings("unused") - @Specialization(guards = {"self.getBuffer() != null", "self.isFastClosedChecks()"}) + @Specialization(guards = {"self.getBuffer() != null", "self.hasFileIORaw()"}) static boolean isClosedFileIO(PBuffered self) { return self.getFileIORaw().isClosed(); } - @Specialization(guards = {"self.getBuffer() != null", "!self.isFastClosedChecks()"}) + @Specialization(guards = {"self.getBuffer() != null", "!self.hasFileIORaw()"}) static boolean isClosedBuffered(VirtualFrame frame, Node inliningTarget, PBuffered self, @Cached PyObjectGetAttr getAttr, @Cached PyObjectIsTrueNode isTrue) { @@ -245,7 +245,7 @@ RawTellNode doIt(@Cached(inline = false) RawTellNode node) { } } - @GenerateInline(inlineByDefault = true) + @GenerateInline @GenerateCached(false) abstract static class RawTellIgnoreErrorNode extends PNodeWithContext { public abstract long execute(VirtualFrame frame, Node inliningTarget, PBuffered self); @@ -337,7 +337,7 @@ protected static void readWrite(VirtualFrame frame, PBuffered self, * implementation of cpython/Modules/_io/bufferedio.c:_io__Buffered_seek_impl */ @GenerateInline - @GenerateCached + @GenerateCached(false) abstract static class SeekNode extends PNodeWithContext { public abstract long execute(VirtualFrame frame, Node inliningTarget, PBuffered self, long off, int whence); @@ -464,7 +464,7 @@ static void finalizing(Node inliningTarget, PBuffered self, * written threaded I/O code. */ if (!self.getLock().acquireTimeout(inliningTarget, (long) 1e3)) { - throw lazyRaise.raise(inliningTarget, SystemError, SHUTDOWN_POSSIBLY_DUE_TO_DAEMON_THREADS); + throw lazyRaise.raise(inliningTarget, SystemError, SHUTDOWN_POSSIBLY_DUE_TO_DAEMON_THREADS, self); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedReaderMixinBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedReaderMixinBuiltins.java index 8f5fb49222..f32954f9e4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedReaderMixinBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedReaderMixinBuiltins.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 diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedWriterNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedWriterNodes.java index 5c7a52b178..9176176bc7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedWriterNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/BufferedWriterNodes.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 @@ -46,23 +46,33 @@ import static com.oracle.graal.python.builtins.modules.io.BufferedIOUtil.isValidWriteBuffer; import static com.oracle.graal.python.builtins.modules.io.BufferedIOUtil.rawOffset; import static com.oracle.graal.python.builtins.modules.io.IONodes.T_WRITE; +import static com.oracle.graal.python.nodes.ErrorMessages.FILE_NOT_OPEN_FOR_S; +import static com.oracle.graal.python.nodes.ErrorMessages.IO_CLOSED; import static com.oracle.graal.python.nodes.ErrorMessages.IO_S_INVALID_LENGTH; import static com.oracle.graal.python.nodes.ErrorMessages.WRITE_COULD_NOT_COMPLETE_WITHOUT_BLOCKING; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.IOUnsupportedOperation; import static com.oracle.graal.python.runtime.exception.PythonErrorType.OSError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError; import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.builtins.modules.PosixModuleBuiltins; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary; import com.oracle.graal.python.builtins.objects.bytes.PBytes; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; +import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.runtime.GilNode; +import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; +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.util.PythonUtils; @@ -74,6 +84,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; public class BufferedWriterNodes { @@ -222,7 +233,40 @@ abstract static class RawWriteNode extends PNodeWithContext { /** * implementation of cpython/Modules/_io/bufferedio.c:_bufferedwriter_raw_write */ - @Specialization + @SuppressWarnings("truffle-sharing") + @Specialization(guards = "self.hasFileIORaw()") + static int bufferedwriterRawWriteFileIO(VirtualFrame frame, Node inliningTarget, PBuffered self, byte[] buf, int len, + @Bind PythonContext context, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached InlinedBranchProfile errorProfile, + @Cached GilNode gil, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, + @Cached PRaiseNode raiseNode) { + PFileIO fileIO = self.getFileIORaw(); + if (fileIO.isClosed()) { + throw raiseNode.raise(inliningTarget, ValueError, IO_CLOSED); + } + if (!fileIO.isWritable()) { + throw raiseNode.raise(inliningTarget, IOUnsupportedOperation, FILE_NOT_OPEN_FOR_S, "writing"); + } + final int n; + try { + n = Math.toIntExact(PosixModuleBuiltins.WriteNode.write(fileIO.getFD(), buf, len, + inliningTarget, posixLib, context.getPosixSupport(), errorProfile, gil)); + } catch (PosixException e) { + if (e.hasErrno(OSErrorEnum.EAGAIN)) { + return -2; + } + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); + } + if (n > 0 && self.getAbsPos() != -1) { + self.incAbsPos(n); + } + return n; + } + + @SuppressWarnings("truffle-sharing") + @Specialization(guards = "!self.hasFileIORaw()") static int bufferedwriterRawWrite(VirtualFrame frame, Node inliningTarget, PBuffered self, byte[] buf, int len, @Bind PythonLanguage language, @Cached PyObjectCallMethodObjArgs callMethod, @@ -272,8 +316,16 @@ protected static void bufferedwriterFlushUnlocked(VirtualFrame frame, PBuffered self.incRawPos(-rewind); } while (self.getWritePos() < self.getWriteEnd()) { - byte[] buf = PythonUtils.arrayCopyOfRange(self.getBuffer(), self.getWritePos(), self.getWriteEnd()); - int n = rawWriteNode.execute(frame, inliningTarget, self, buf, buf.length); + byte[] buf; + int len; + if (self.hasFileIORaw() && self.getWritePos() == 0) { + buf = self.getBuffer(); + len = self.getWriteEnd(); + } else { + buf = PythonUtils.arrayCopyOfRange(self.getBuffer(), self.getWritePos(), self.getWriteEnd()); + len = buf.length; + } + int n = rawWriteNode.execute(frame, inliningTarget, self, buf, len); if (n == -2) { throw raiseBlockingIOError.get(inliningTarget).raiseEAGAIN(WRITE_COULD_NOT_COMPLETE_WITHOUT_BLOCKING, 0); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/FileIOBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/FileIOBuiltins.java index 0371ba1b45..b308aaf531 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/FileIOBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/FileIOBuiltins.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 @@ -74,7 +74,7 @@ import static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.createOutputStream; import static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.toByteArray; import static com.oracle.graal.python.builtins.objects.exception.OSErrorEnum.EAGAIN; -import static com.oracle.graal.python.nodes.BuiltinNames.J_OPEN; +import static com.oracle.graal.python.nodes.BuiltinNames.T_OPEN; import static com.oracle.graal.python.nodes.ErrorMessages.BAD_MODE; import static com.oracle.graal.python.nodes.ErrorMessages.CANNOT_USE_CLOSEFD; import static com.oracle.graal.python.nodes.ErrorMessages.EMBEDDED_NULL_BYTE; @@ -176,7 +176,6 @@ @CoreFunctions(extendClasses = PythonBuiltinClassType.PFileIO) public final class FileIOBuiltins extends PythonBuiltins { - /* * We are limited to max primitive array size, Integer.MAX_VALUE, the jdk can offer. CPython * defines the max value as system's SSIZE_T_MAX on linux and INT_MAX for MacOS and Windows. @@ -202,9 +201,6 @@ public FDReleaseCallback(OwnFD fd) { @Override public void execute(PythonContext context, Access access) { - if (fd.isReleased()) { - return; - } try { fd.doRelease(); } catch (Exception e) { @@ -271,7 +267,7 @@ private static int open(VirtualFrame frame, TruffleString name, int flags, int m } } catch (PosixException e) { errorProfile.enter(inliningTarget); - if (e.getErrorCode() == OSErrorEnum.EINTR.getNumber()) { + if (e.hasErrno(OSErrorEnum.EINTR)) { PythonContext.triggerAsyncActions(inliningTarget); } else { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e, name); @@ -365,7 +361,7 @@ static void doInit(VirtualFrame frame, Node inliningTarget, PFileIO self, Object } int flags = processMode(self, mode); - auditNode.audit(inliningTarget, J_OPEN, nameobj, mode.mode, flags); + auditNode.audit(frame, inliningTarget, T_OPEN, nameobj, mode.mode, flags); try { boolean fdIsOwn = false; @@ -431,7 +427,7 @@ static void doInit(VirtualFrame frame, Node inliningTarget, PFileIO self, Object * Tolerate fstat() errors other than EBADF. See Issue #25717, where an * anonymous file on a Virtual Box shared folder filesystem would raise ENOENT. */ - if (e.getErrorCode() == OSErrorEnum.EBADF.getNumber()) { + if (e.hasErrno(OSErrorEnum.EBADF)) { errorCleanup(frame, self, fdIsOwn, posixClose); throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } @@ -456,7 +452,7 @@ static void doInit(VirtualFrame frame, Node inliningTarget, PFileIO self, Object if (self.getSeekable() < 0) { self.setSeekable(0); } - if (e.getErrorCode() != OSErrorEnum.ESPIPE.getNumber()) { + if (!e.hasErrno(OSErrorEnum.ESPIPE)) { errorCleanup(frame, self, fdIsOwn, posixClose); throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } @@ -548,7 +544,7 @@ static Object read(VirtualFrame frame, PFileIO self, int size, try { return PosixModuleBuiltins.ReadNode.read(self.getFD(), size, inliningTarget, posixLib, context.getPosixSupport(), readErrorProfile, gil); } catch (PosixException e) { - if (e.getErrorCode() == EAGAIN.getNumber()) { + if (e.hasErrno(EAGAIN)) { readErrorProfile2.enter(inliningTarget); return PNone.NONE; } @@ -614,7 +610,7 @@ static Object readall(VirtualFrame frame, PFileIO self, return b; } } catch (PosixException e) { - if (e.getErrorCode() == EAGAIN.getNumber()) { + if (e.hasErrno(EAGAIN)) { return PNone.NONE; } throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); @@ -646,7 +642,7 @@ static Object readall(VirtualFrame frame, PFileIO self, break; } } catch (PosixException e) { - if (e.getErrorCode() == EAGAIN.getNumber()) { + if (e.hasErrno(EAGAIN)) { if (bytesRead > 0) { break; } @@ -695,7 +691,7 @@ static Object readinto(VirtualFrame frame, PFileIO self, Object buffer, bufferLib.readIntoBuffer(data, 0, buffer, 0, n, bufferLib); return n; } catch (PosixException e) { - if (e.getErrorCode() == EAGAIN.getNumber()) { + if (e.hasErrno(EAGAIN)) { return PNone.NONE; } throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); @@ -751,7 +747,7 @@ static Object write(VirtualFrame frame, PFileIO self, Object buffer, return PosixModuleBuiltins.WriteNode.write(self.getFD(), bufferLib.getInternalOrCopiedByteArray(buffer), bufferLib.getBufferLength(buffer), inliningTarget, posixLib, context.getPosixSupport(), errorProfile, gil); } catch (PosixException e) { - if (e.getErrorCode() == EAGAIN.getNumber()) { + if (e.hasErrno(EAGAIN)) { return PNone.NONE; } throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PBuffered.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PBuffered.java index bbcf567d5c..bf71cb80ba 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PBuffered.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PBuffered.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 @@ -158,7 +158,7 @@ public void setFinalizing(boolean finalizing) { this.finalizing = finalizing; } - public boolean isFastClosedChecks() { + public boolean hasFileIORaw() { return fileioRaw != null; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PFileIO.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PFileIO.java index 89fdb4eac1..f2ff239b55 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PFileIO.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PFileIO.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 @@ -91,7 +91,9 @@ public void setFD(int fd, PythonContext context) { public void setClosed() { if (fd != null) { if (fd.getOwnFD() != null) { - fd.getOwnFD().markReleased(); + boolean markedReleased = fd.getOwnFD().markReleased(); + assert markedReleased || fd.getOwnFD().isReleased(); + } fd = null; } @@ -208,11 +210,13 @@ public OwnFD(Object referent, int fd, PythonContext context) { @SuppressWarnings("try") void doRelease() { - markReleased(); - try (GilNode.UncachedRelease gil = GilNode.uncachedRelease()) { - PosixSupportLibrary.getUncached().close(context.getPosixSupport(), (int) getReference()); - } catch (PosixException e) { - // ignore + if (markReleased()) { + assert isReleased(); + try (GilNode.UncachedRelease gil = GilNode.uncachedRelease()) { + PosixSupportLibrary.getUncached().close(context.getPosixSupport(), (int) getReference()); + } catch (PosixException e) { + // ignore + } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PTextIO.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PTextIO.java index 200ff15b56..155ac163ad 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PTextIO.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PTextIO.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,13 +41,9 @@ package com.oracle.graal.python.builtins.modules.io; import static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.append; -import static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.createOutputStream; -import static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.toByteArray; import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; -import java.io.ByteArrayOutputStream; - 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; @@ -93,7 +89,7 @@ public final class PTextIO extends PTextIOBase { private int decodedCharsUsed; /* offset (in code points) into _decoded_chars for read() */ private int decodedCharsLen; /* code point length of decodedChars */ - private ByteArrayOutputStream pendingBytes; // data waiting to be written. + private PendingBytesOutputStream pendingBytes; // data waiting to be written. /* * snapshot is either NULL, or a tuple (dec_flags, next_input) where dec_flags is the second @@ -112,7 +108,7 @@ public final class PTextIO extends PTextIOBase { public PTextIO(Object cls, Shape instanceShape) { super(cls, instanceShape); - pendingBytes = createOutputStream(); + pendingBytes = new PendingBytesOutputStream(); } @Override @@ -324,11 +320,11 @@ TruffleString consumeAllDecodedChars(TruffleString.SubstringNode substringNode, } public void clearPendingBytes() { - pendingBytes = createOutputStream(); + pendingBytes = new PendingBytesOutputStream(); } - public byte[] getAndClearPendingBytes() { - byte[] b = toByteArray(pendingBytes); + public PendingBytesOutputStream getAndClearPendingBytes() { + PendingBytesOutputStream b = pendingBytes; clearPendingBytes(); return b; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/LazyBouncyCastleProvider.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PendingBytesOutputStream.java similarity index 77% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/LazyBouncyCastleProvider.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PendingBytesOutputStream.java index a4e15fc0d1..809711b9f3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/LazyBouncyCastleProvider.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PendingBytesOutputStream.java @@ -1,5 +1,5 @@ /* - * 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 @@ -38,21 +38,25 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.oracle.graal.python.builtins.objects.ssl; +package com.oracle.graal.python.builtins.modules.io; -import java.security.Provider; -import java.security.Security; +import java.io.ByteArrayOutputStream; -import org.bouncycastle.jce.provider.BouncyCastleProvider; +final class PendingBytesOutputStream extends ByteArrayOutputStream { -public final class LazyBouncyCastleProvider { - private static Provider securityProvider; + PendingBytesOutputStream() { + super(); + } + + PendingBytesOutputStream(int size) { + super(size); + } + + byte[] getBuffer() { + return buf; + } - public static synchronized Provider initProvider() { - if (securityProvider == null) { - securityProvider = new BouncyCastleProvider(); - Security.addProvider(securityProvider); - } - return securityProvider; + int getCount() { + return count; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java index 5eb40a3447..71d9832ae4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.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 @@ -207,8 +207,8 @@ static void nothingTodo(@SuppressWarnings("unused") PTextIO self) { static void writeflush(VirtualFrame frame, Node inliningTarget, PTextIO self, @Bind PythonLanguage language, @Cached PyObjectCallMethodObjArgs callMethod) { - byte[] pending = self.getAndClearPendingBytes(); - PBytes b = PFactory.createBytes(language, pending); + PendingBytesOutputStream pending = self.getAndClearPendingBytes(); + PBytes b = PFactory.createBytes(language, pending.getBuffer(), pending.getCount()); callMethod.execute(frame, inliningTarget, self.getBuffer(), T_WRITE, b); // TODO: check _PyIO_trap_eintr } @@ -884,7 +884,7 @@ static void init(VirtualFrame frame, Node inliningTarget, PTextIO self, Object b if (buffer instanceof PBuffered) { /* Cache the raw FileIO object to speed up 'closed' checks */ - if (((PBuffered) buffer).isFastClosedChecks()) { + if (((PBuffered) buffer).hasFileIORaw()) { PFileIO f = ((PBuffered) buffer).getFileIORaw(); self.setFileIO(f); } 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/json/JSONScannerBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java index b1829a6411..cae62531f6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.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 @@ -6,6 +6,7 @@ package com.oracle.graal.python.builtins.modules.json; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RecursionError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.objects.str.StringUtils.byteIndexToCodepointIndex; import static com.oracle.graal.python.builtins.objects.str.StringUtils.codepointIndexToByteIndex; import static com.oracle.graal.python.nodes.StringLiterals.T_STRICT; @@ -36,6 +37,7 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.PyCallableCheckNode; import com.oracle.graal.python.lib.PyFloatCheckExactNode; import com.oracle.graal.python.lib.PyLongCheckExactNode; import com.oracle.graal.python.lib.PyLongFromUnicodeObject; @@ -59,6 +61,7 @@ import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage; import com.oracle.graal.python.util.ArrayBuilder; 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; import com.oracle.truffle.api.dsl.Bind; @@ -107,6 +110,7 @@ public abstract static class MakeScanner extends PythonBinaryBuiltinNode { public PJSONScanner doNew(VirtualFrame frame, Object cls, Object context, @Bind Node inliningTarget, @Cached PyObjectIsTrueNode castStrict, + @Cached PyCallableCheckNode checkCallable, @Cached TypeNodes.GetInstanceShape getInstanceShape, @Cached PyFloatCheckExactNode pyFloatCheckExactNode, @Cached PyLongCheckExactNode pyLongCheckExactNode) { @@ -119,6 +123,19 @@ public PJSONScanner doNew(VirtualFrame frame, Object cls, Object context, Object parseConstant = getParseConstant.execute(frame, context); Object parseFloat = pyFloatCheckExactNode.execute(inliningTarget, parseFloatProp) ? PNone.NONE : parseFloatProp; Object parseInt = pyLongCheckExactNode.execute(inliningTarget, parseIntProp) ? PNone.NONE : parseIntProp; + /* + * Validate that object_hook and object_pairs_hook, if provided, are callable. This + * mirrors CPython's behavior and prevents ClassCastException when a non‑callable (e.g., + * a string) is supplied. + */ + if (objectHook != PNone.NONE && !checkCallable.execute(inliningTarget, objectHook)) { + CompilerDirectives.transferToInterpreter(); + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_MUST_BE_CALLABLE, "object_hook"); + } + if (objectPairsHook != PNone.NONE && !checkCallable.execute(inliningTarget, objectPairsHook)) { + CompilerDirectives.transferToInterpreter(); + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.S_MUST_BE_CALLABLE, "object_pairs_hook"); + } return PFactory.createJSONScanner(cls, getInstanceShape.execute(cls), strict, objectHook, objectPairsHook, parseFloat, parseInt, parseConstant); } } @@ -445,6 +462,7 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall final Object value; ScannerState nextState = null; if (state == ScannerState.dict) { + /* scanner is currently inside a dictionary */ int c; if (idx >= length || (c = codePointAtIndexNode.execute(string, idx)) != '"' && (c != '}')) { throw decodeError(frame, boundaryCallData, inliningTarget, errorProfile, this, string, idx, ErrorMessages.EXPECTING_PROP_NAME_ECLOSED_IN_DBL_QUOTES); @@ -457,6 +475,17 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall Object topOfStack = stack.pop(); TruffleString parentKey = null; final Object dict; + /* + * If no hooks are present, the stack contains only the parent dicts or + * lists the current object is nested in. If a objectHook or objectPairsHook + * is present, the stack also stores the property keys of nested dicts. For + * example, when parsing the innermost dict of '{"a":{"b":{"c":"d"}}}', the + * stack will contain (from bottom to top) [, , + * "a", , "b"]. This is necessary so after finishing parsing a + * dict, we can call the hook and replace it with the hook's return value in + * the parent dict, i.e. when in the previous example we finish parsing + * '{"c":"d"}', we effectively execute parent["b"] = objectHook({"c":"d"}). + */ if (hasPairsHook) { if (topOfStack instanceof TruffleString) { parentKey = (TruffleString) topOfStack; @@ -484,6 +513,9 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall nextState = parentKey == null ? ScannerState.list : ScannerState.dict; if (nextState == ScannerState.dict) { Object parent = stack.peek(); + if (parent instanceof TruffleString) { + parent = stack.get(stack.size() - 2); + } if (hasPairsHook) { currentPairsStorage = (ObjectSequenceStorage) parent; currentPairsStorage.setObjectItemNormalized(currentPairsStorage.length() - 1, PFactory.createTuple(language, new Object[]{parentKey, dict})); @@ -518,6 +550,7 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall substringByteIndexNode, appendCodePointNode, appendSubstringByteIndexNode, builderToStringNode); + /* force hash computation */ hashCodeNode.execute(newKey, TS_ENCODING); TruffleString key = memoPutIfAbsent(memo, newKey); if (key == null) { @@ -624,12 +657,20 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall assert nextState == ScannerState.dict; currentPairsStorage = (ObjectSequenceStorage) value; if (state == ScannerState.dict) { + /* + * save the associated propertyKey so we can replace the current + * value with the hook's result later + */ stack.add(propertyKey); } } else { assert nextState == ScannerState.dict; currentDictStorage = (EconomicMapStorage) ((PDict) value).getDictStorage(); if (hasObjectHook && state == ScannerState.dict) { + /* + * save the associated propertyKey so we can replace the current + * value with the hook's result later + */ stack.add(propertyKey); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMACompressorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMACompressorBuiltins.java index 9e2ab7f7be..b7b8487192 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMACompressorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMACompressorBuiltins.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 @@ -57,11 +57,11 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; import com.oracle.graal.python.annotations.ArgumentClinic.ClinicConversion; +import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.annotations.ClinicConverterFactory; 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; @@ -119,7 +119,7 @@ LZMAObject doNew(Object cls, @SuppressWarnings("unused") Object arg, @Cached TypeNodes.GetInstanceShape getInstanceShape) { // data filled in subsequent __init__ call - see LZMACompressorBuiltins.InitNode PythonContext context = getContext(); - return PFactory.createLZMACompressor(cls, getInstanceShape.execute(cls), context.getNFILZMASupport().isAvailable()); + return PFactory.createLZMACompressor(cls, getInstanceShape.execute(cls), context.getNativeLZMASupport().isAvailable()); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMADecompressorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMADecompressorBuiltins.java index 9c63aaf441..4b330e66a4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMADecompressorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMADecompressorBuiltins.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 @@ -57,10 +57,10 @@ 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; @@ -116,7 +116,7 @@ LZMAObject doNew(Object cls, @SuppressWarnings("unused") Object arg, @Cached TypeNodes.GetInstanceShape getInstanceShape) { // data filled in subsequent __init__ call - see LZMADecompressorBuiltins.InitNode PythonContext context = getContext(); - return PFactory.createLZMADecompressor(cls, getInstanceShape.execute(cls), context.getNFILZMASupport().isAvailable()); + return PFactory.createLZMADecompressor(cls, getInstanceShape.execute(cls), context.getNativeLZMASupport().isAvailable()); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMAModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMAModuleBuiltins.java index a7a53a5abb..fd6feb5169 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMAModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMAModuleBuiltins.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 @@ -41,34 +41,34 @@ package com.oracle.graal.python.builtins.modules.lzma; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; -import static com.oracle.graal.python.runtime.NFILZMASupport.CHECK_CRC32_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.CHECK_CRC64_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.CHECK_ID_MAX_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.CHECK_NONE_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.CHECK_SHA256_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.CHECK_UNKNOWN_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_ARMTHUMB_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_ARM_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_DELTA_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_IA64_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_LZMA1_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_LZMA2_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_POWERPC_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_SPARC_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FILTER_X86_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FORMAT_ALONE_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FORMAT_AUTO_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FORMAT_RAW_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.FORMAT_XZ_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.MF_BT2_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.MF_BT3_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.MF_BT4_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.MF_HC3_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.MF_HC4_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.MODE_FAST_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.MODE_NORMAL_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.PRESET_DEFAULT_INDEX; -import static com.oracle.graal.python.runtime.NFILZMASupport.PRESET_EXTREME_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.CHECK_CRC32_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.CHECK_CRC64_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.CHECK_ID_MAX_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.CHECK_NONE_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.CHECK_SHA256_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.CHECK_UNKNOWN_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_ARMTHUMB_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_ARM_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_DELTA_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_IA64_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_LZMA1_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_LZMA2_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_POWERPC_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_SPARC_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FILTER_X86_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FORMAT_ALONE_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FORMAT_AUTO_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FORMAT_RAW_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.FORMAT_XZ_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.MF_BT2_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.MF_BT3_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.MF_BT4_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.MF_HC3_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.MF_HC4_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.MODE_FAST_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.MODE_NORMAL_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.PRESET_DEFAULT_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.PRESET_EXTREME_INDEX; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.util.List; @@ -92,8 +92,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.util.CastToJavaLongLossyNode; -import com.oracle.graal.python.runtime.NFILZMASupport; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeLZMASupport; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.dsl.Bind; @@ -182,7 +181,7 @@ public void initialize(Python3Core core) { @Override public void postInitialize(Python3Core c) { super.postInitialize(c); - NFILZMASupport lzmaSupport = c.getContext().getNFILZMASupport(); + NativeLZMASupport lzmaSupport = c.getContext().getNativeLZMASupport(); PythonModule lzmaModule = c.lookupBuiltinModule(T__LZMA); int[] formats = new int[4]; int[] checks = new int[6]; @@ -213,8 +212,8 @@ public void postInitialize(Python3Core c) { MODE_NORMAL = modes[MODE_NORMAL_INDEX]; PRESET_DEFAULT = preset[PRESET_DEFAULT_INDEX]; PRESET_EXTREME = preset[PRESET_EXTREME_INDEX]; - } catch (NativeLibrary.NativeLibraryCannotBeLoaded e) { - lzmaSupport.notAvailable(); + } catch (UnsupportedOperationException e) { + lzmaSupport.setNotAvailable(); // ignore and proceed without native lzma support and use the java port. } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMANodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMANodes.java index f95782af19..04bc5f667e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMANodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMANodes.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 @@ -65,9 +65,9 @@ import static com.oracle.graal.python.nodes.ErrorMessages.INVALID_FILTER_CHAIN_FOR_FORMAT; import static com.oracle.graal.python.nodes.ErrorMessages.VALUE_TOO_LARGE_TO_FIT_INTO_INDEX; import static com.oracle.graal.python.nodes.StringLiterals.T_ID; -import static com.oracle.graal.python.runtime.NFILZMASupport.LZMA_ID_ERROR; -import static com.oracle.graal.python.runtime.NFILZMASupport.LZMA_PRESET_ERROR; -import static com.oracle.graal.python.runtime.NFILZMASupport.MAX_OPTS_INDEX; +import static com.oracle.graal.python.runtime.NativeLZMASupport.LZMA_ID_ERROR; +import static com.oracle.graal.python.runtime.NativeLZMASupport.LZMA_PRESET_ERROR; +import static com.oracle.graal.python.runtime.NativeLZMASupport.MAX_OPTS_INDEX; 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.tsLiteral; @@ -117,8 +117,7 @@ import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode; import com.oracle.graal.python.nodes.util.CastToJavaLongLossyNode; -import com.oracle.graal.python.runtime.NFILZMASupport; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeLZMASupport; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PythonErrorType; import com.oracle.graal.python.util.OverflowException; @@ -489,35 +488,29 @@ long[][] parseFilter(VirtualFrame frame, Object filterSpecs, @GenerateInline(false) // footprint reduction 40 -> 21 protected abstract static class NativeFilterChain extends Node { - public abstract void execute(VirtualFrame frame, Object lzmast, PythonContext context, Object filterSpecs); + public abstract void execute(VirtualFrame frame, long lzmast, PythonContext context, Object filterSpecs); @Specialization - static void parseFilterChainSpec(VirtualFrame frame, Object lzmast, PythonContext context, Object filterSpecs, + static void parseFilterChainSpec(VirtualFrame frame, long lzmast, PythonContext context, Object filterSpecs, @Bind Node inliningTarget, - @Cached NativeLibrary.InvokeNativeFunction setFilterSpecLZMA, - @Cached NativeLibrary.InvokeNativeFunction setFilterSpecDelta, - @Cached NativeLibrary.InvokeNativeFunction setFilterSpecBCJ, @Cached LZMAParseFilterChain parseFilterChain, @Cached PRaiseNode raiseNode) { long[][] filters = parseFilterChain.execute(frame, filterSpecs); for (int i = 0; i < filters.length; i++) { - setFilterOptions(inliningTarget, lzmast, filters[i], i, context, setFilterSpecLZMA, setFilterSpecDelta, setFilterSpecBCJ, raiseNode); + setFilterOptions(inliningTarget, lzmast, filters[i], i, context, raiseNode); } } - private static void setFilterOptions(Node inliningTarget, Object lzmast, long[] filter, int fidx, PythonContext context, - NativeLibrary.InvokeNativeFunction setFilterSpecLZMA, - NativeLibrary.InvokeNativeFunction setFilterSpecDelta, - NativeLibrary.InvokeNativeFunction setFilterSpecBCJ, + private static void setFilterOptions(Node inliningTarget, long lzmast, long[] filter, int fidx, PythonContext context, PRaiseNode raiseNode) { - NFILZMASupport lzmaSupport = context.getNFILZMASupport(); + NativeLZMASupport lzmaSupport = context.getNativeLZMASupport(); int err; long id = filter[0]; switch (LZMAFilter.from(id)) { case LZMA_FILTER_LZMA1: case LZMA_FILTER_LZMA2: assert filter.length == 10; - err = lzmaSupport.setFilterSpecLZMA(lzmast, fidx, filter, setFilterSpecLZMA); + err = lzmaSupport.setFilterSpecLZMA(lzmast, fidx, filter); if (err != LZMA_OK) { if (err == LZMA_PRESET_ERROR) { throw raiseNode.raise(inliningTarget, LZMAError, INVALID_COMPRESSION_PRESET, filter[LZMAOption.preset.ordinal()]); @@ -527,7 +520,7 @@ private static void setFilterOptions(Node inliningTarget, Object lzmast, long[] return; case LZMA_FILTER_DELTA: assert filter.length == 2; - err = lzmaSupport.setFilterSpecDelta(lzmast, fidx, filter, setFilterSpecDelta); + err = lzmaSupport.setFilterSpecDelta(lzmast, fidx, filter); if (err != LZMA_OK) { errorHandling(inliningTarget, err, raiseNode); } @@ -539,7 +532,7 @@ private static void setFilterOptions(Node inliningTarget, Object lzmast, long[] case LZMA_FILTER_ARMTHUMB: case LZMA_FILTER_SPARC: assert filter.length == 2; - err = lzmaSupport.setFilterSpecBCJ(lzmast, fidx, filter, setFilterSpecBCJ); + err = lzmaSupport.setFilterSpecBCJ(lzmast, fidx, filter); if (err != LZMA_OK) { errorHandling(inliningTarget, err, raiseNode); } @@ -688,14 +681,12 @@ public abstract static class LZMACompressInit extends Node { @Specialization(guards = "format == FORMAT_XZ") static void xz(LZMACompressor.Native self, @SuppressWarnings("unused") int format, long preset, @SuppressWarnings("unused") PNone filters, @Bind Node inliningTarget, - @Shared("cs") @Cached NativeLibrary.InvokeNativeFunction createStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction lzmaEasyEncoder, @Shared @Cached InlinedConditionProfile errProfile, @Shared @Cached PRaiseNode raiseNode) { - NFILZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); - int lzret = lzmaSupport.lzmaEasyEncoder(lzmast, preset, self.getCheck(), lzmaEasyEncoder); + int lzret = lzmaSupport.lzmaEasyEncoder(lzmast, preset, self.getCheck()); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -704,17 +695,15 @@ static void xz(LZMACompressor.Native self, @SuppressWarnings("unused") int forma @Specialization(guards = {"format == FORMAT_XZ", "!isPNone(filters)"}) static void xz(VirtualFrame frame, LZMACompressor.Native self, @SuppressWarnings("unused") int format, @SuppressWarnings("unused") long preset, Object filters, @Bind Node inliningTarget, - @Shared("cs") @Cached NativeLibrary.InvokeNativeFunction createStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction lzmaStreamEncoder, @Exclusive @Cached NativeFilterChain filterChain, @Shared @Cached InlinedConditionProfile errProfile, @Shared @Cached PRaiseNode raiseNode) { PythonContext ctxt = PythonContext.get(inliningTarget); - NFILZMASupport lzmaSupport = ctxt.getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = ctxt.getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); filterChain.execute(frame, lzmast, ctxt, filters); - int lzret = lzmaSupport.lzmaStreamEncoder(lzmast, self.getCheck(), lzmaStreamEncoder); + int lzret = lzmaSupport.lzmaStreamEncoder(lzmast, self.getCheck()); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -724,14 +713,12 @@ static void xz(VirtualFrame frame, LZMACompressor.Native self, @SuppressWarnings @Specialization(guards = "format == FORMAT_ALONE") static void alone(LZMACompressor.Native self, int format, long preset, PNone filters, @Bind Node inliningTarget, - @Shared("cs") @Cached NativeLibrary.InvokeNativeFunction createStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction lzmaAloneEncoderPreset, @Shared @Cached InlinedConditionProfile errProfile, @Shared @Cached PRaiseNode raiseNode) { - NFILZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); - int lzret = lzmaSupport.lzmaAloneEncoderPreset(lzmast, preset, lzmaAloneEncoderPreset); + int lzret = lzmaSupport.lzmaAloneEncoderPreset(lzmast, preset); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -741,17 +728,15 @@ static void alone(LZMACompressor.Native self, int format, long preset, PNone fil @Specialization(guards = {"format == FORMAT_ALONE", "!isPNone(filters)"}) static void alone(VirtualFrame frame, LZMACompressor.Native self, int format, long preset, Object filters, @Bind Node inliningTarget, - @Shared("cs") @Cached NativeLibrary.InvokeNativeFunction createStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction lzmaAloneEncoder, @Exclusive @Cached NativeFilterChain filterChain, @Shared @Cached InlinedConditionProfile errProfile, @Shared @Cached PRaiseNode raiseNode) { PythonContext ctxt = PythonContext.get(inliningTarget); - NFILZMASupport lzmaSupport = ctxt.getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = ctxt.getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); filterChain.execute(frame, lzmast, ctxt, filters); - int lzret = lzmaSupport.lzmaAloneEncoder(lzmast, lzmaAloneEncoder); + int lzret = lzmaSupport.lzmaAloneEncoder(lzmast); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -761,17 +746,15 @@ static void alone(VirtualFrame frame, LZMACompressor.Native self, int format, lo @Specialization(guards = "format == FORMAT_RAW") static void raw(VirtualFrame frame, LZMACompressor.Native self, int format, long preset, Object filters, @Bind Node inliningTarget, - @Shared("cs") @Cached NativeLibrary.InvokeNativeFunction createStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction lzmaRawEncoder, @Exclusive @Cached NativeFilterChain filterChain, @Shared @Cached InlinedConditionProfile errProfile, @Shared @Cached PRaiseNode raiseNode) { PythonContext ctxt = PythonContext.get(inliningTarget); - NFILZMASupport lzmaSupport = ctxt.getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = ctxt.getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); filterChain.execute(frame, lzmast, ctxt, filters); - int lzret = lzmaSupport.lzmaRawEncoder(lzmast, lzmaRawEncoder); + int lzret = lzmaSupport.lzmaRawEncoder(lzmast); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -871,12 +854,11 @@ public byte[] flush(Node inliningTarget, LZMACompressor self, PythonContext cont @Specialization static byte[] nativeCompress(Node inliningTarget, LZMACompressor.Native self, PythonContext context, byte[] bytes, int len, int action, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction compress, @Cached GetOutputNativeBufferNode getBuffer, @Cached InlinedConditionProfile errProfile, @Exclusive @Cached PRaiseNode raiseNode) { - NFILZMASupport lzmaSupport = context.getNFILZMASupport(); - int err = lzmaSupport.compress(self.getLzs(), bytes, len, action, INITIAL_BUFFER_SIZE, compress); + NativeLZMASupport lzmaSupport = context.getNativeLZMASupport(); + int err = lzmaSupport.compress(self.getLzs(), bytes, len, action, INITIAL_BUFFER_SIZE); if (errProfile.profile(inliningTarget, err != LZMA_OK)) { errorHandling(inliningTarget, err, raiseNode); } @@ -924,15 +906,13 @@ static void init(LZMADecompressor.Java self, int format, int memlimit) { @Specialization(guards = "format == FORMAT_AUTO") static void auto(LZMADecompressor.Native self, @SuppressWarnings("unused") int format, int memlimit, @Bind Node inliningTarget, - @Shared("cs") @Cached NativeLibrary.InvokeNativeFunction createStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction lzmaAutoDecoder, @Shared @Cached InlinedConditionProfile errProfile, @Shared @Cached PRaiseNode raiseNode) { - NFILZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); int decoderFlags = LZMA_TELL_ANY_CHECK | LZMA_TELL_NO_CHECK; - int lzret = lzmaSupport.lzmaAutoDecoder(lzmast, memlimit, decoderFlags, lzmaAutoDecoder); + int lzret = lzmaSupport.lzmaAutoDecoder(lzmast, memlimit, decoderFlags); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -941,15 +921,13 @@ static void auto(LZMADecompressor.Native self, @SuppressWarnings("unused") int f @Specialization(guards = "format == FORMAT_XZ") static void xz(LZMADecompressor.Native self, @SuppressWarnings("unused") int format, int memlimit, @Bind Node inliningTarget, - @Shared("cs") @Cached NativeLibrary.InvokeNativeFunction createStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction lzmaStreamDecoder, @Shared @Cached InlinedConditionProfile errProfile, @Shared @Cached PRaiseNode raiseNode) { - NFILZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); int decoderFlags = LZMA_TELL_ANY_CHECK | LZMA_TELL_NO_CHECK; - int lzret = lzmaSupport.lzmaStreamDecoder(lzmast, memlimit, decoderFlags, lzmaStreamDecoder); + int lzret = lzmaSupport.lzmaStreamDecoder(lzmast, memlimit, decoderFlags); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -958,14 +936,12 @@ static void xz(LZMADecompressor.Native self, @SuppressWarnings("unused") int for @Specialization(guards = "format == FORMAT_ALONE") static void alone(LZMADecompressor.Native self, @SuppressWarnings("unused") int format, int memlimit, @Bind Node inliningTarget, - @Shared("cs") @Cached NativeLibrary.InvokeNativeFunction createStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction lzmaAloneDecoder, @Shared @Cached InlinedConditionProfile errProfile, @Shared @Cached PRaiseNode raiseNode) { - NFILZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = PythonContext.get(inliningTarget).getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); - int lzret = lzmaSupport.lzmaAloneDecoder(lzmast, memlimit, lzmaAloneDecoder); + int lzret = lzmaSupport.lzmaAloneDecoder(lzmast, memlimit); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -988,17 +964,15 @@ static void rawJava(VirtualFrame frame, LZMADecompressor.Java self, Object filte @Specialization static void rawNative(VirtualFrame frame, Node inliningTarget, LZMADecompressor.Native self, Object filters, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction createStream, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction lzmaRawDecoder, @Cached(inline = false) NativeFilterChain filterChain, @Cached InlinedConditionProfile errProfile, @Cached PRaiseNode raiseNode) { PythonContext context = PythonContext.get(inliningTarget); - NFILZMASupport lzmaSupport = context.getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = context.getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); self.init(lzmast, lzmaSupport); filterChain.execute(frame, lzmast, context, filters); - int lzret = lzmaSupport.lzmaRawDecoder(lzmast, lzmaRawDecoder); + int lzret = lzmaSupport.lzmaRawDecoder(lzmast); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { errorHandling(inliningTarget, lzret, raiseNode); } @@ -1107,23 +1081,18 @@ public abstract static class InternalDecompressNode extends PNodeWithContext { @Specialization static byte[] nativeInternalDecompress(Node inliningTarget, LZMADecompressor.Native self, int maxLength, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction decompress, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getLzsAvailIn, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getLzsAvailOut, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getNextInIndex, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getLzsCheck, @Cached GetOutputNativeBufferNode getBuffer, @Exclusive @Cached PRaiseNode lazyRaiseNode, @Cached InlinedConditionProfile errProfile) { PythonContext context = PythonContext.get(inliningTarget); - NFILZMASupport lzmaSupport = context.getNFILZMASupport(); + NativeLZMASupport lzmaSupport = context.getNativeLZMASupport(); byte[] inGuest = self.getNextIn(); int offset = self.getNextInIndex(); - int err = lzmaSupport.decompress(self.getLzs(), inGuest, offset, maxLength, INITIAL_BUFFER_SIZE, self.getLzsAvailIn(), decompress); - long nextInIdx = lzmaSupport.getNextInIndex(self.getLzs(), getNextInIndex); - long lzsAvailIn = lzmaSupport.getLzsAvailIn(self.getLzs(), getLzsAvailIn); - long lzsAvailOut = lzmaSupport.getLzsAvailOut(self.getLzs(), getLzsAvailOut); - self.setCheck(lzmaSupport.getLzsCheck(self.getLzs(), getLzsCheck)); + int err = lzmaSupport.decompress(self.getLzs(), inGuest, offset, maxLength, INITIAL_BUFFER_SIZE, self.getLzsAvailIn()); + long nextInIdx = lzmaSupport.getNextInIndex(self.getLzs()); + long lzsAvailIn = lzmaSupport.getLzsAvailIn(self.getLzs()); + long lzsAvailOut = lzmaSupport.getLzsAvailOut(self.getLzs()); + self.setCheck(lzmaSupport.getLzsCheck(self.getLzs())); try { self.setNextInIndex(nextInIdx); self.setLzsAvailIn(lzsAvailIn); @@ -1222,17 +1191,15 @@ private static void doDecompression(LZMADecompressor.Java self, ByteArrayOutputS @GenerateCached(false) public abstract static class GetOutputNativeBufferNode extends Node { - public abstract byte[] execute(Node inliningTarget, Object lzmast, PythonContext context); + public abstract byte[] execute(Node inliningTarget, long lzmast, PythonContext context); @Specialization - static byte[] getBuffer(Node inliningTarget, Object lzmast, PythonContext context, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getBufferSize, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getBuffer, + static byte[] getBuffer(Node inliningTarget, long lzmast, PythonContext context, @Cached PRaiseNode raiseNode) { - NFILZMASupport lzmaSupport = context.getNFILZMASupport(); + NativeLZMASupport lzmaSupport = context.getNativeLZMASupport(); int size; try { - size = PInt.intValueExact(lzmaSupport.getOutputBufferSize(lzmast, getBufferSize)); + size = PInt.intValueExact(lzmaSupport.getOutputBufferSize(lzmast)); } catch (OverflowException of) { throw raiseNode.raise(inliningTarget, SystemError, VALUE_TOO_LARGE_TO_FIT_INTO_INDEX); } @@ -1241,7 +1208,7 @@ static byte[] getBuffer(Node inliningTarget, Object lzmast, PythonContext contex } byte[] resultArray = new byte[size]; /* this will clear the native output once retrieved */ - lzmaSupport.getOutputBuffer(lzmast, resultArray, getBuffer); + lzmaSupport.getOutputBuffer(lzmast, resultArray); return resultArray; } } @@ -1254,13 +1221,12 @@ public abstract static class IsCheckSupported extends Node { @NonIdempotent protected boolean useNativeContext() { - return PythonContext.get(this).getNFILZMASupport().isAvailable(); + return PythonContext.get(this).getNativeLZMASupport().isAvailable(); } @Specialization(guards = "useNativeContext()") - static boolean checkNative(Node inliningTarget, int checkId, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction checkIsSupported) { - return PythonContext.get(inliningTarget).getNFILZMASupport().checkIsSupported(checkId, checkIsSupported) == 1; + static boolean checkNative(Node inliningTarget, int checkId) { + return PythonContext.get(inliningTarget).getNativeLZMASupport().checkIsSupported(checkId) == 1; } @TruffleBoundary @@ -1282,7 +1248,7 @@ public abstract static class EncodeFilterProperties extends Node { @NonIdempotent protected boolean useNativeContext() { - return PythonContext.get(this).getNFILZMASupport().isAvailable(); + return PythonContext.get(this).getNativeLZMASupport().isAvailable(); } @Specialization(guards = "useNativeContext()") @@ -1290,25 +1256,22 @@ static byte[] encodeNative(VirtualFrame frame, Object filter, @Bind Node inliningTarget, @Cached LZMAFilterConverter filterConverter, @Cached GetOutputNativeBufferNode getBuffer, - @Cached NativeLibrary.InvokeNativeFunction createStream, - @Cached NativeLibrary.InvokeNativeFunction encodeFilter, - @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Cached InlinedConditionProfile errProfile, @Cached PRaiseNode raiseNode) { PythonContext ctxt = PythonContext.get(inliningTarget); - NFILZMASupport lzmaSupport = ctxt.getNFILZMASupport(); - Object lzmast = lzmaSupport.createStream(createStream); + NativeLZMASupport lzmaSupport = ctxt.getNativeLZMASupport(); + long lzmast = lzmaSupport.createStream(); long[] opts = filterConverter.execute(frame, filter); - int lzret = lzmaSupport.encodeFilter(lzmast, opts, encodeFilter); + int lzret = lzmaSupport.encodeFilter(lzmast, opts); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { - lzmaSupport.deallocateStream(lzmast, deallocateStream); + lzmaSupport.deallocateStream(lzmast); if (lzret == LZMA_PRESET_ERROR) { throw raiseNode.raise(inliningTarget, LZMAError, INVALID_COMPRESSION_PRESET, opts[LZMAOption.preset.ordinal()]); } errorHandling(inliningTarget, lzret, raiseNode); } byte[] encoded = getBuffer.execute(inliningTarget, lzmast, ctxt); - lzmaSupport.deallocateStream(lzmast, deallocateStream); + lzmaSupport.deallocateStream(lzmast); return encoded; } @@ -1327,21 +1290,20 @@ public abstract static class DecodeFilterProperties extends Node { @NonIdempotent protected boolean useNativeContext() { - return PythonContext.get(this).getNFILZMASupport().isAvailable(); + return PythonContext.get(this).getNativeLZMASupport().isAvailable(); } @Specialization(guards = "useNativeContext()") static void decodeNative(VirtualFrame frame, long id, byte[] encoded, PDict dict, @Bind Node inliningTarget, - @Cached NativeLibrary.InvokeNativeFunction decodeFilter, @Cached HashingStorageSetItem setItem, @Cached InlinedConditionProfile errProfile, @Cached PRaiseNode raiseNode) { PythonContext ctxt = PythonContext.get(inliningTarget); - NFILZMASupport lzmaSupport = ctxt.getNFILZMASupport(); + NativeLZMASupport lzmaSupport = ctxt.getNativeLZMASupport(); long[] opts = new long[MAX_OPTS_INDEX]; int len = encoded.length; - int lzret = lzmaSupport.decodeFilter(id, encoded, len, opts, decodeFilter); + int lzret = lzmaSupport.decodeFilter(id, encoded, len, opts); if (errProfile.profile(inliningTarget, lzret != LZMA_OK)) { if (lzret == LZMA_ID_ERROR) { throw raiseNode.raise(inliningTarget, LZMAError, INVALID_FILTER, opts[LZMAOption.id.ordinal()]); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMAObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMAObject.java index de4181604f..6b98ac4554 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMAObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/lzma/LZMAObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, 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 @@ -63,7 +63,7 @@ import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; -import com.oracle.graal.python.runtime.NFILZMASupport; +import com.oracle.graal.python.runtime.NativeLZMASupport; import com.oracle.graal.python.util.OverflowException; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -77,11 +77,11 @@ public LZMAObject(Object cls, Shape instanceShape) { super(cls, instanceShape); } - public void setCheck(int check) { + public final void setCheck(int check) { this.check = check; } - public int getCheck() { + public final int getCheck() { return check; } @@ -102,7 +102,7 @@ public void setFlushed() { this.flushed = true; } - public static class Java extends LZMACompressor { + public static final class Java extends LZMACompressor { private FinishableOutputStream lzmaStream; private final ByteArrayOutputStream bos; @@ -151,30 +151,35 @@ public void resetBuffer() { } } - public static class Native extends LZMACompressor { + public static final class Native extends LZMACompressor { - private NFILZMASupport.Pointer pointer; + private NativeLZMASupport.Pointer pointer; public Native(Object cls, Shape instanceShape) { super(cls, instanceShape); } - public final void init(Object lzmast, NFILZMASupport lib) { - this.pointer = new NFILZMASupport.Pointer(this, lzmast, lib); + public void init(long lzmast, NativeLZMASupport lib) { + this.pointer = new NativeLZMASupport.Pointer(this, lzmast, lib); + assert !pointer.isReleased(); } - public final Object getLzs() { - assert pointer != null; - return pointer.getReference(); + public long getLzs() { + NativeLZMASupport.Pointer p = pointer; + assert p != null && !p.isReleased(); + return p.getPointer(); } @TruffleBoundary - public final void markReleased() { - if (pointer != null) { - synchronized (this) { - pointer.markReleased(); - pointer = null; - } + public void markReleased() { + NativeLZMASupport.Pointer p; + synchronized (this) { + p = pointer; + pointer = null; + } + if (p != null) { + boolean markedReleased = p.markReleased(); + assert markedReleased || p.isReleased(); } } } @@ -210,65 +215,65 @@ public LZMADecompressor(Object cls, Shape instanceShape) { this.nextInIndex = 0; } - public void setMemlimit(int memlimit) { + public final void setMemlimit(int memlimit) { this.memlimit = memlimit; } - public int getMemlimit() { + public final int getMemlimit() { return memlimit; } - public void setFormat(int format) { + public final void setFormat(int format) { this.format = format; } - public boolean isEOF() { + public final boolean isEOF() { return eof; } - public void setEOF() { + public final void setEOF() { this.eof = true; } - public void setEOF(boolean b) { + public final void setEOF(boolean b) { this.eof = b; } - public byte[] getUnusedData() { + public final byte[] getUnusedData() { return unusedData; } - public void setUnusedData() { + public final void setUnusedData() { this.unusedData = Arrays.copyOfRange(nextIn, nextInIndex, nextInIndex + lzsAvailIn); } - public boolean needsInput() { + public final boolean needsInput() { return needsInput; } - public void setNeedsInput(boolean needsInput) { + public final void setNeedsInput(boolean needsInput) { this.needsInput = needsInput; } - public byte[] getInputBuffer() { + public final byte[] getInputBuffer() { return inputBuffer; } - public void setInputBuffer(byte[] inputBuffer) { + public final void setInputBuffer(byte[] inputBuffer) { this.inputBuffer = inputBuffer; } - public void createInputBuffer(int size) { + public final void createInputBuffer(int size) { this.inputBuffer = new byte[size]; this.inputBufferSize = size; } - public void discardInputBuffer() { + public final void discardInputBuffer() { this.inputBuffer = null; this.inputBufferSize = 0; } - public void resizeInputBuffer(int size) { + public final void resizeInputBuffer(int size) { assert size >= inputBufferSize; byte[] tmp = new byte[size]; if (inputBuffer != null && lzsAvailIn != 0) { @@ -278,68 +283,68 @@ public void resizeInputBuffer(int size) { this.inputBufferSize = size; } - public int getInputBufferSize() { + public final int getInputBufferSize() { return inputBufferSize; } - public void setInputBufferSize(int inputBufferSize) { + public final void setInputBufferSize(int inputBufferSize) { this.inputBufferSize = inputBufferSize; } - public byte[] getNextIn() { + public final byte[] getNextIn() { return nextIn; } - public void setNextIn(byte[] in) { + public final void setNextIn(byte[] in) { assert in != null; this.nextIn = in; } - public void clearNextIn() { + public final void clearNextIn() { this.nextIn = null; } - public int getNextInIndex() { + public final int getNextInIndex() { return nextInIndex; } - public void setNextInIndex(int nextInIndex) { + public final void setNextInIndex(int nextInIndex) { this.nextInIndex = nextInIndex; } - public void setNextInIndex(long nextInIndex) throws OverflowException { + public final void setNextInIndex(long nextInIndex) throws OverflowException { this.nextInIndex = PInt.intValueExact(nextInIndex); } - public int getLzsAvailIn() { + public final int getLzsAvailIn() { return lzsAvailIn; } - public int getLzsAvailOut() { + public final int getLzsAvailOut() { return lzsAvailOut; } - public void incLzsAvailIn(int size) { + public final void incLzsAvailIn(int size) { this.lzsAvailIn += size; } - public void setLzsAvailIn(int lzsAvailIn) { + public final void setLzsAvailIn(int lzsAvailIn) { this.lzsAvailIn = lzsAvailIn; } - public void setLzsAvailIn(long lzsAvailIn) throws OverflowException { + public final void setLzsAvailIn(long lzsAvailIn) throws OverflowException { this.lzsAvailIn = PInt.intValueExact(lzsAvailIn); } - public void setLzsAvailOut(int lzsAvailOut) { + public final void setLzsAvailOut(int lzsAvailOut) { this.lzsAvailOut = lzsAvailOut; } - public void setLzsAvailOut(long lzsAvailOut) throws OverflowException { + public final void setLzsAvailOut(long lzsAvailOut) throws OverflowException { this.lzsAvailOut = PInt.intValueExact(lzsAvailOut); } - public static class Java extends LZMADecompressor { + public static final class Java extends LZMADecompressor { private LZMANodes.LZMAByteInputStream input; private InputStream lzs; @@ -403,7 +408,7 @@ public void update(int availOut) { } @TruffleBoundary - protected static LZMANodes.LZMAByteInputStream createLZMAByteInputStream(byte[] buf, int offset, int length) { + private static LZMANodes.LZMAByteInputStream createLZMAByteInputStream(byte[] buf, int offset, int length) { return new LZMANodes.LZMAByteInputStream(buf, offset, length); } @@ -453,30 +458,35 @@ public void createLZMA() throws IOException { } } - public static class Native extends LZMADecompressor { + public static final class Native extends LZMADecompressor { - private NFILZMASupport.Pointer pointer; + private NativeLZMASupport.Pointer pointer; public Native(Object cls, Shape instanceShape) { super(cls, instanceShape); } - public final void init(Object lzmast, NFILZMASupport lib) { - this.pointer = new NFILZMASupport.Pointer(this, lzmast, lib); + public void init(long lzmast, NativeLZMASupport lib) { + this.pointer = new NativeLZMASupport.Pointer(this, lzmast, lib); + assert !pointer.isReleased(); } - public final Object getLzs() { - assert pointer != null; - return pointer.getReference(); + public long getLzs() { + NativeLZMASupport.Pointer p = pointer; + assert p != null && !p.isReleased(); + return p.getPointer(); } @TruffleBoundary - public final void markReleased() { - if (pointer != null) { - synchronized (this) { - pointer.markReleased(); - pointer = null; - } + public void markReleased() { + NativeLZMASupport.Pointer p; + synchronized (this) { + p = pointer; + pointer = null; + } + if (p != null) { + boolean markedReleased = p.markReleased(); + assert markedReleased || p.isReleased(); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/multiprocessing/SemLockBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/multiprocessing/SemLockBuiltins.java index bd125c05a5..974672c697 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/multiprocessing/SemLockBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/multiprocessing/SemLockBuiltins.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 @@ -50,10 +50,10 @@ import java.util.List; 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.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltinClassType; @@ -364,11 +364,11 @@ int get(VirtualFrame frame, PSemLock self, sval = 0; } return sval; - } catch (PosixException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } catch (UnsupportedPosixFeatureException e) { // Not available on Darwin throw raiseNode.raise(inliningTarget, NotImplementedError); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PPickler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PPickler.java index 21d27a0e36..6c6d389fec 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PPickler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PPickler.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 @@ -99,6 +99,7 @@ import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectSizeNode; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; @@ -603,6 +604,10 @@ private int getHashingStorageLength(HashingStorage storage) { return hashingStorageLenNode.executeCached(storage); } + private boolean isTuple(Object object) { + return PyTupleCheckNode.executeUncached(object); + } + private void save(VirtualFrame frame, PPickler pickler, Object obj, int persSave) { if (recursiveSaveNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -787,7 +792,7 @@ private void saveIteratorBatchedUnrolled(VirtualFrame frame, PPickler pickler, O private void saveDictIteratorBatchUnrolled(VirtualFrame frame, PPickler pickler, Object iterator) { saveIteratorBatchedUnrolled(frame, pickler, iterator, PickleUtils.OPCODE_SETITEM, PickleUtils.OPCODE_SETITEMS, (Object item) -> { - if (!(item instanceof PTuple) || length(frame, item) != 2) { + if (!isTuple(item) || length(frame, item) != 2) { throw raise(TypeError, ErrorMessages.MUST_S_ITER_RETURN_2TUPLE, DICT_ITEMS); } }, @@ -832,7 +837,7 @@ private void saveFrozenSetIterator(VirtualFrame frame, PPickler pickler, Object private void saveDictIterator(VirtualFrame frame, PPickler pickler, Object iterator) { saveIterator(frame, pickler, iterator, PickleUtils.OPCODE_SETITEM, (Object item) -> { - if (!(item instanceof PTuple) || length(frame, item) != 2) { + if (!isTuple(item) || length(frame, item) != 2) { throw raise(TypeError, ErrorMessages.MUST_S_ITER_RETURN_2TUPLE, DICT_ITEMS); } save(frame, pickler, getItem(frame, item, 0), 0); @@ -945,7 +950,7 @@ private void handleReduce(VirtualFrame frame, BoundaryCallData boundaryCallData, return; } - if (!(reduceValue instanceof PTuple)) { + if (!isTuple(reduceValue)) { throw raise(PicklingError, ErrorMessages.S_MUST_RETURN_S_OR_S, T___REDUCE__, "string", "tuple"); } @@ -958,7 +963,7 @@ private void saveReduce(VirtualFrame frame, PythonContext ctx, PPickler pickler, boolean useNewobj = false, useNewobjEx = false; - if (!(arguments instanceof PTuple)) { + if (!isTuple(arguments)) { throw raise(PythonBuiltinClassType.SystemError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC); } int size = length(frame, arguments); @@ -977,7 +982,7 @@ private void saveReduce(VirtualFrame frame, PythonContext ctx, PPickler pickler, throw raise(PicklingError, ErrorMessages.S_ITEM_REDUCE_MUST_BE_S, "first", "callable"); } - if (!(argtup instanceof PTuple)) { + if (!isTuple(argtup)) { throw raise(PicklingError, ErrorMessages.S_ITEM_REDUCE_MUST_BE_S, "second", "a tuple"); } @@ -1033,7 +1038,7 @@ private void saveReduce(VirtualFrame frame, PythonContext ctx, PPickler pickler, } args = getItem(frame, argtup, 1); - if (!(args instanceof PTuple)) { + if (!isTuple(args)) { throw raise(PicklingError, ErrorMessages.S_ITEM_FROM_S_MUST_BE_S_NOT_P, "second", "NEWOBJ_EX", "a tuple", args); } @@ -1327,7 +1332,7 @@ private void saveBytes(VirtualFrame frame, PythonContext ctx, PPickler pickler, reduceValue = createTuple(ctx.getCore().lookupType(PythonBuiltinClassType.PBytes), createTuple()); } else { PickleState st = getGlobalState(ctx.getCore()); - final TruffleString unicodeStr = PickleUtils.decodeLatin1Strict(getBufferLibrary().getCopiedByteArray(buffer), ensureTsFromByteArray(), ensureTsSwitchEncodingNode()); + final TruffleString unicodeStr = PickleUtils.decodeLatin1Strict(getBufferLibrary().getCopiedByteArray(buffer), ensureTsFromByteArrayWithCompaction()); reduceValue = createTuple(st.codecsEncode, createTuple(unicodeStr, LATIN1)); } // save_reduce() will memoize the object automatically. diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PUnpickler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PUnpickler.java index 635f4e2020..4c00d8ac08 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PUnpickler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PUnpickler.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 @@ -871,7 +871,7 @@ private void loadInt(VirtualFrame frame, PUnpickler self) { } try { - long x = PickleUtils.asciiBytesToLong(s, ensureTsParseLongNode(), ensureTsFromByteArray()); + long x = PickleUtils.asciiBytesToLong(s, ensureTsParseLongNode(), ensureTsFromByteArrayWithCompaction()); if (s.length == 3 && (x == 0 || x == 1)) { value = x != 0; } else if (x == (int) x) { @@ -900,7 +900,7 @@ private void loadLong(VirtualFrame frame, PUnpickler self) { s[s.length - 2] = 0; } try { - value = PickleUtils.asciiBytesToLong(s, ensureTsParseLongNode(), ensureTsFromByteArray()); + value = PickleUtils.asciiBytesToLong(s, ensureTsParseLongNode(), ensureTsFromByteArrayWithCompaction()); } catch (TruffleString.NumberFormatException nfe) { value = parseInt(s); } @@ -1505,7 +1505,7 @@ private void loadGet(VirtualFrame frame, PUnpickler self) { } int idx; try { - idx = PickleUtils.asciiBytesToInt(s, ensureTsParseIntNode(), ensureTsFromByteArray()); + idx = PickleUtils.asciiBytesToInt(s, ensureTsParseIntNode(), ensureTsFromByteArrayWithCompaction()); } catch (TruffleString.NumberFormatException nfe) { // TODO handle exception [GR-38101] throw CompilerDirectives.shouldNotReachHere(); @@ -1572,7 +1572,7 @@ private void loadPut(VirtualFrame frame, PUnpickler self) { Object value = self.stack.data[self.stack.size - 1]; int idx; try { - idx = PickleUtils.asciiBytesToInt(s, ensureTsParseIntNode(), ensureTsFromByteArray()); + idx = PickleUtils.asciiBytesToInt(s, ensureTsParseIntNode(), ensureTsFromByteArrayWithCompaction()); } catch (TruffleString.NumberFormatException nfe) { // TODO handle exception [GR-38101] throw CompilerDirectives.shouldNotReachHere(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PickleUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PickleUtils.java index 683df9bc07..276a5c3d0c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PickleUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PickleUtils.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 @@ -257,21 +257,21 @@ public static int getStringSize(byte[] bytes) { } public static TruffleString getValidIntString(byte[] bytes) { - return getValidIntASCIIString(bytes, TruffleString.FromByteArrayNode.getUncached()); + return getValidIntASCIIString(bytes, TruffleString.FromByteArrayWithCompactionUTF32Node.getUncached()); } - public static int asciiBytesToInt(byte[] bytes, TruffleString.ParseIntNode parseIntNode, TruffleString.FromByteArrayNode fromByteArrayNode) + public static int asciiBytesToInt(byte[] bytes, TruffleString.ParseIntNode parseIntNode, TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) throws TruffleString.NumberFormatException { return parseIntNode.execute(getValidIntASCIIString(bytes, fromByteArrayNode), 10); } - public static long asciiBytesToLong(byte[] bytes, TruffleString.ParseLongNode parseLongNode, TruffleString.FromByteArrayNode fromByteArrayNode) + public static long asciiBytesToLong(byte[] bytes, TruffleString.ParseLongNode parseLongNode, TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) throws TruffleString.NumberFormatException { return parseLongNode.execute(getValidIntASCIIString(bytes, fromByteArrayNode), 10); } - private static TruffleString getValidIntASCIIString(byte[] bytes, TruffleString.FromByteArrayNode fromByteArray) { - return fromByteArray.execute(bytes, 0, getStringSize(bytes), TruffleString.Encoding.US_ASCII, true); + private static TruffleString getValidIntASCIIString(byte[] bytes, TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArray) { + return fromByteArray.execute(bytes, 0, getStringSize(bytes), TruffleString.CompactionLevel.S1, true); } @TruffleBoundary @@ -316,8 +316,8 @@ public static TruffleString decodeUTF8Strict(byte[] data, int len, TruffleString return decodeStrict(data, len, fromByteArrayNode, TruffleString.Encoding.UTF_8, switchEncodingNode); } - public static TruffleString decodeLatin1Strict(byte[] data, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingNode) { - return decodeStrict(data, data.length, fromByteArrayNode, TruffleString.Encoding.ISO_8859_1, switchEncodingNode); + public static TruffleString decodeLatin1Strict(byte[] data, TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { + return fromByteArrayNode.execute(data, 0, data.length, TruffleString.CompactionLevel.S1, true); } private static TruffleString decodeStrict(byte[] data, int len, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.Encoding encoding, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PicklerNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PicklerNodes.java index e838b83e36..6e38abab05 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PicklerNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pickle/PicklerNodes.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 @@ -152,6 +152,7 @@ abstract static class BasePickleNode extends Node { @Child private BytesNodes.ToBytesNode toBytesNode; @Child private PyObjectReprAsTruffleStringNode reprNode; @Child private TruffleString.FromByteArrayNode tsFromByteArrayNode; + @Child private TruffleString.FromByteArrayWithCompactionUTF32Node tsFromByteArrayWithCompactionNode; @Child private TruffleString.CodePointLengthNode tsCodePointLengthNode; @Child private TruffleString.CodePointAtIndexUTF32Node tsCodePointAtIndexUTF32Node; @Child private TruffleString.FromLongNode tsFromLongNode; @@ -190,6 +191,14 @@ protected TruffleString.FromByteArrayNode ensureTsFromByteArray() { return tsFromByteArrayNode; } + protected TruffleString.FromByteArrayWithCompactionUTF32Node ensureTsFromByteArrayWithCompaction() { + if (tsFromByteArrayWithCompactionNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + tsFromByteArrayWithCompactionNode = insert(TruffleString.FromByteArrayWithCompactionUTF32Node.create()); + } + return tsFromByteArrayWithCompactionNode; + } + protected TruffleString.CodePointLengthNode ensureTsCodePointLengthNode() { if (tsCodePointLengthNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/PXMLParser.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/PXMLParser.java new file mode 100644 index 0000000000..eb5e92c661 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/PXMLParser.java @@ -0,0 +1,461 @@ +/* + * 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.builtins.modules.pyexpat; + +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; +import com.oracle.truffle.api.object.Shape; +import com.oracle.truffle.api.strings.TruffleString; + +public final class PXMLParser extends PythonBuiltinObject { + public static final int XML_PARAM_ENTITY_PARSING_NEVER = 0; + public static final int XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE = 1; + public static final int XML_PARAM_ENTITY_PARSING_ALWAYS = 2; + + public static final int XML_ERROR_FINISHED = 1; + public static final int XML_ERROR_SYNTAX = 2; + public static final int XML_ERROR_UNCLOSED_TOKEN = 3; + public static final int XML_ERROR_AMPLIFICATION_LIMIT_BREACH = 43; + public static final String XML_ERROR_AMPLIFICATION_LIMIT_BREACH_MESSAGE = "limit on input amplification factor (from DTD and entities) breached"; + + private final TruffleString namespaceSeparator; + + private boolean bufferText; + private boolean namespacePrefixes; + private boolean orderedAttributes; + private boolean specifiedAttributes; + private int bufferSize = 8192; + private int bufferUsed; + + private int currentByteIndex; + private int currentLineNumber = 1; + private int currentColumnNumber; + private int errorByteIndex; + private int errorLineNumber = 1; + private int errorColumnNumber; + + private boolean finished; + private boolean foreignDTD; + private int paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; + private boolean reparseDeferralEnabled = true; + private int deliveredEventCount; + + private byte[] data = new byte[0]; + private TruffleString base; + private TruffleString encoding; + private Object intern; + + private Object startElementHandler = PNone.NONE; + private Object endElementHandler = PNone.NONE; + private Object processingInstructionHandler = PNone.NONE; + private Object characterDataHandler = PNone.NONE; + private Object unparsedEntityDeclHandler = PNone.NONE; + private Object notationDeclHandler = PNone.NONE; + private Object startNamespaceDeclHandler = PNone.NONE; + private Object endNamespaceDeclHandler = PNone.NONE; + private Object commentHandler = PNone.NONE; + private Object startCdataSectionHandler = PNone.NONE; + private Object endCdataSectionHandler = PNone.NONE; + private Object defaultHandler = PNone.NONE; + private Object defaultHandlerExpand = PNone.NONE; + private Object notStandaloneHandler = PNone.NONE; + private Object externalEntityRefHandler = PNone.NONE; + private Object startDoctypeDeclHandler = PNone.NONE; + private Object endDoctypeDeclHandler = PNone.NONE; + private Object entityDeclHandler = PNone.NONE; + private Object xmlDeclHandler = PNone.NONE; + private Object elementDeclHandler = PNone.NONE; + private Object attlistDeclHandler = PNone.NONE; + private Object skippedEntityHandler = PNone.NONE; + + public PXMLParser(Object cls, Shape instanceShape, TruffleString namespaceSeparator) { + super(cls, instanceShape); + this.namespaceSeparator = namespaceSeparator; + } + + public TruffleString getNamespaceSeparator() { + return namespaceSeparator; + } + + public boolean isBufferText() { + return bufferText; + } + + public void setBufferText(boolean bufferText) { + this.bufferText = bufferText; + } + + public boolean isNamespacePrefixes() { + return namespacePrefixes; + } + + public void setNamespacePrefixes(boolean namespacePrefixes) { + this.namespacePrefixes = namespacePrefixes; + } + + public boolean isOrderedAttributes() { + return orderedAttributes; + } + + public void setOrderedAttributes(boolean orderedAttributes) { + this.orderedAttributes = orderedAttributes; + } + + public boolean isSpecifiedAttributes() { + return specifiedAttributes; + } + + public void setSpecifiedAttributes(boolean specifiedAttributes) { + this.specifiedAttributes = specifiedAttributes; + } + + public int getBufferSize() { + return bufferSize; + } + + public void setBufferSize(int bufferSize) { + this.bufferSize = bufferSize; + } + + public int getBufferUsed() { + return bufferUsed; + } + + public void setBufferUsed(int bufferUsed) { + this.bufferUsed = bufferUsed; + } + + public int getCurrentByteIndex() { + return currentByteIndex; + } + + public void setCurrentByteIndex(int currentByteIndex) { + this.currentByteIndex = currentByteIndex; + } + + public int getCurrentLineNumber() { + return currentLineNumber; + } + + public void setCurrentLineNumber(int currentLineNumber) { + this.currentLineNumber = currentLineNumber; + } + + public int getCurrentColumnNumber() { + return currentColumnNumber; + } + + public void setCurrentColumnNumber(int currentColumnNumber) { + this.currentColumnNumber = currentColumnNumber; + } + + public int getErrorByteIndex() { + return errorByteIndex; + } + + public void setErrorByteIndex(int errorByteIndex) { + this.errorByteIndex = errorByteIndex; + } + + public int getErrorLineNumber() { + return errorLineNumber; + } + + public void setErrorLineNumber(int errorLineNumber) { + this.errorLineNumber = errorLineNumber; + } + + public int getErrorColumnNumber() { + return errorColumnNumber; + } + + public void setErrorColumnNumber(int errorColumnNumber) { + this.errorColumnNumber = errorColumnNumber; + } + + public boolean isFinished() { + return finished; + } + + public void setFinished(boolean finished) { + this.finished = finished; + } + + public boolean isForeignDTD() { + return foreignDTD; + } + + public void setForeignDTD(boolean foreignDTD) { + this.foreignDTD = foreignDTD; + } + + public int getParamEntityParsing() { + return paramEntityParsing; + } + + public void setParamEntityParsing(int paramEntityParsing) { + this.paramEntityParsing = paramEntityParsing; + } + + public boolean isReparseDeferralEnabled() { + return reparseDeferralEnabled; + } + + public void setReparseDeferralEnabled(boolean reparseDeferralEnabled) { + this.reparseDeferralEnabled = reparseDeferralEnabled; + } + + public int getDeliveredEventCount() { + return deliveredEventCount; + } + + public void setDeliveredEventCount(int deliveredEventCount) { + this.deliveredEventCount = deliveredEventCount; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + public TruffleString getBase() { + return base; + } + + public void setBase(TruffleString base) { + this.base = base; + } + + public TruffleString getEncoding() { + return encoding; + } + + public void setEncoding(TruffleString encoding) { + this.encoding = encoding; + } + + public Object getIntern() { + return intern; + } + + public void setIntern(Object intern) { + this.intern = intern; + } + + public Object getStartElementHandler() { + return startElementHandler; + } + + public void setStartElementHandler(Object startElementHandler) { + this.startElementHandler = startElementHandler; + } + + public Object getEndElementHandler() { + return endElementHandler; + } + + public void setEndElementHandler(Object endElementHandler) { + this.endElementHandler = endElementHandler; + } + + public Object getProcessingInstructionHandler() { + return processingInstructionHandler; + } + + public void setProcessingInstructionHandler(Object processingInstructionHandler) { + this.processingInstructionHandler = processingInstructionHandler; + } + + public Object getCharacterDataHandler() { + return characterDataHandler; + } + + public void setCharacterDataHandler(Object characterDataHandler) { + this.characterDataHandler = characterDataHandler; + } + + public Object getUnparsedEntityDeclHandler() { + return unparsedEntityDeclHandler; + } + + public void setUnparsedEntityDeclHandler(Object unparsedEntityDeclHandler) { + this.unparsedEntityDeclHandler = unparsedEntityDeclHandler; + } + + public Object getNotationDeclHandler() { + return notationDeclHandler; + } + + public void setNotationDeclHandler(Object notationDeclHandler) { + this.notationDeclHandler = notationDeclHandler; + } + + public Object getStartNamespaceDeclHandler() { + return startNamespaceDeclHandler; + } + + public void setStartNamespaceDeclHandler(Object startNamespaceDeclHandler) { + this.startNamespaceDeclHandler = startNamespaceDeclHandler; + } + + public Object getEndNamespaceDeclHandler() { + return endNamespaceDeclHandler; + } + + public void setEndNamespaceDeclHandler(Object endNamespaceDeclHandler) { + this.endNamespaceDeclHandler = endNamespaceDeclHandler; + } + + public Object getCommentHandler() { + return commentHandler; + } + + public void setCommentHandler(Object commentHandler) { + this.commentHandler = commentHandler; + } + + public Object getStartCdataSectionHandler() { + return startCdataSectionHandler; + } + + public void setStartCdataSectionHandler(Object startCdataSectionHandler) { + this.startCdataSectionHandler = startCdataSectionHandler; + } + + public Object getEndCdataSectionHandler() { + return endCdataSectionHandler; + } + + public void setEndCdataSectionHandler(Object endCdataSectionHandler) { + this.endCdataSectionHandler = endCdataSectionHandler; + } + + public Object getDefaultHandler() { + return defaultHandler; + } + + public void setDefaultHandler(Object defaultHandler) { + this.defaultHandler = defaultHandler; + } + + public Object getDefaultHandlerExpand() { + return defaultHandlerExpand; + } + + public void setDefaultHandlerExpand(Object defaultHandlerExpand) { + this.defaultHandlerExpand = defaultHandlerExpand; + } + + public Object getNotStandaloneHandler() { + return notStandaloneHandler; + } + + public void setNotStandaloneHandler(Object notStandaloneHandler) { + this.notStandaloneHandler = notStandaloneHandler; + } + + public Object getExternalEntityRefHandler() { + return externalEntityRefHandler; + } + + public void setExternalEntityRefHandler(Object externalEntityRefHandler) { + this.externalEntityRefHandler = externalEntityRefHandler; + } + + public Object getStartDoctypeDeclHandler() { + return startDoctypeDeclHandler; + } + + public void setStartDoctypeDeclHandler(Object startDoctypeDeclHandler) { + this.startDoctypeDeclHandler = startDoctypeDeclHandler; + } + + public Object getEndDoctypeDeclHandler() { + return endDoctypeDeclHandler; + } + + public void setEndDoctypeDeclHandler(Object endDoctypeDeclHandler) { + this.endDoctypeDeclHandler = endDoctypeDeclHandler; + } + + public Object getEntityDeclHandler() { + return entityDeclHandler; + } + + public void setEntityDeclHandler(Object entityDeclHandler) { + this.entityDeclHandler = entityDeclHandler; + } + + public Object getXmlDeclHandler() { + return xmlDeclHandler; + } + + public void setXmlDeclHandler(Object xmlDeclHandler) { + this.xmlDeclHandler = xmlDeclHandler; + } + + public Object getElementDeclHandler() { + return elementDeclHandler; + } + + public void setElementDeclHandler(Object elementDeclHandler) { + this.elementDeclHandler = elementDeclHandler; + } + + public Object getAttlistDeclHandler() { + return attlistDeclHandler; + } + + public void setAttlistDeclHandler(Object attlistDeclHandler) { + this.attlistDeclHandler = attlistDeclHandler; + } + + public Object getSkippedEntityHandler() { + return skippedEntityHandler; + } + + public void setSkippedEntityHandler(Object skippedEntityHandler) { + this.skippedEntityHandler = skippedEntityHandler; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/PyExpatModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/PyExpatModuleBuiltins.java new file mode 100644 index 0000000000..032f2a4cbe --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/PyExpatModuleBuiltins.java @@ -0,0 +1,184 @@ +/* + * 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.builtins.modules.pyexpat; + +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; +import static com.oracle.graal.python.nodes.StringLiterals.T_JAVA; +import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; + +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.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.builtins.objects.PNone; +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.type.PythonBuiltinClass; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; +import com.oracle.graal.python.nodes.function.PythonBuiltinNode; +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; +import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; + +@CoreFunctions(defineModule = "pyexpat") +public final class PyExpatModuleBuiltins extends PythonBuiltins { + private static final TruffleString T_CODES = tsLiteral("codes"); + private static final TruffleString T_MESSAGES = tsLiteral("messages"); + private static final TruffleString T_PARSER_CREATE = tsLiteral("ParserCreate"); + private static final TruffleString T_NAMESPACE_SEPARATOR = tsLiteral("'namespace_separator'"); + private static final TruffleString T_STR_OR_NONE = tsLiteral("str or None"); + + @Override + protected List> getNodeFactories() { + return PyExpatModuleBuiltinsFactory.getFactories(); + } + + @Override + public void initialize(Python3Core core) { + PythonLanguage language = core.getLanguage(); + addBuiltinConstant(T___DOC__, """ + Interface to pyexpat parser (Java backend). + + NOTE: This backend currently provides best-effort compatibility with native Expat. + Known incompatibilities include Expat-specific incremental buffering/chunking behavior, + exact error code/message mapping, and some DTD/external entity callback semantics. + """); + addBuiltinConstant("EXPAT_VERSION", T_JAVA); + addBuiltinConstant("version_info", PFactory.createTuple(language, new Object[]{2, 6, 0})); + addBuiltinConstant("native_encoding", toTruffleStringUncached("UTF-8")); + + addBuiltinConstant("XML_PARAM_ENTITY_PARSING_NEVER", PXMLParser.XML_PARAM_ENTITY_PARSING_NEVER); + addBuiltinConstant("XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE", PXMLParser.XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE); + addBuiltinConstant("XML_PARAM_ENTITY_PARSING_ALWAYS", PXMLParser.XML_PARAM_ENTITY_PARSING_ALWAYS); + + PythonBuiltinClass expatErrorType = core.lookupType(PythonBuiltinClassType.PyExpatError); + addBuiltinConstant("error", expatErrorType); + addBuiltinConstant("ExpatError", expatErrorType); + + PythonModule errors = PFactory.createPythonModule(toTruffleStringUncached("pyexpat.errors")); + PDict codes = PFactory.createDict(language); + PDict messages = PFactory.createDict(language); + addError(errors, codes, messages, "XML_ERROR_FINISHED", "parsing finished", PXMLParser.XML_ERROR_FINISHED); + addError(errors, codes, messages, "XML_ERROR_SYNTAX", "syntax error", PXMLParser.XML_ERROR_SYNTAX); + addError(errors, codes, messages, "XML_ERROR_UNCLOSED_TOKEN", "unclosed token", PXMLParser.XML_ERROR_UNCLOSED_TOKEN); + addError(errors, codes, messages, "XML_ERROR_AMPLIFICATION_LIMIT_BREACH", PXMLParser.XML_ERROR_AMPLIFICATION_LIMIT_BREACH_MESSAGE, + PXMLParser.XML_ERROR_AMPLIFICATION_LIMIT_BREACH); + errors.setAttribute(T_CODES, codes); + errors.setAttribute(T_MESSAGES, messages); + addBuiltinConstant("errors", errors); + + PythonModule model = PFactory.createPythonModule(toTruffleStringUncached("pyexpat.model")); + addBuiltinConstant("model", model); + + super.initialize(core); + } + + @Override + public void postInitialize(Python3Core core) { + super.postInitialize(core); + addBuiltinConstant("features", PFactory.createList(core.getLanguage())); + } + + private static void addError(PythonModule errors, PDict codes, PDict messages, String constName, String msg, int code) { + TruffleString msgTs = toTruffleStringUncached(msg); + errors.setAttribute(toTruffleStringUncached(constName), msgTs); + codes.setItem(msgTs, code); + messages.setItem(code, msgTs); + } + + @Builtin(name = "ErrorString", minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class ErrorStringNode extends PythonBuiltinNode { + + private static final TruffleString T_PARSING_FINISHED = tsLiteral("parsing finished"); + private static final TruffleString T_UNCLOSED_TOKEN = tsLiteral("unclosed token"); + private static final TruffleString T_SYNTAX_ERROR = tsLiteral("syntax error"); + private static final TruffleString T_AMPLIFICATION_LIMIT_BREACH = tsLiteral(PXMLParser.XML_ERROR_AMPLIFICATION_LIMIT_BREACH_MESSAGE); + + @Specialization + static TruffleString doIt(int code) { + return switch (code) { + case PXMLParser.XML_ERROR_FINISHED -> T_PARSING_FINISHED; + case PXMLParser.XML_ERROR_UNCLOSED_TOKEN -> T_UNCLOSED_TOKEN; + case PXMLParser.XML_ERROR_AMPLIFICATION_LIMIT_BREACH -> T_AMPLIFICATION_LIMIT_BREACH; + default -> T_SYNTAX_ERROR; + }; + } + } + + @Builtin(name = "ParserCreate", minNumOfPositionalArgs = 0, parameterNames = {"encoding", "namespace_separator", "intern"}) + @GenerateNodeFactory + abstract static class ParserCreateNode extends PythonBuiltinNode { + @Specialization + static Object create(Object encoding, Object namespaceSeparator, Object intern, + @Bind Node inliningTarget, + @Cached XMLParserBuiltins.CreateParserNode createParserNode, + @Cached PRaiseNode raiseNode) { + Object sep = namespaceSeparator == PNone.NO_VALUE ? PNone.NONE : namespaceSeparator; + if (sep != PNone.NONE && !(sep instanceof TruffleString)) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.S_BRACKETS_ARG_S_MUST_BE_S_NOT_P, T_PARSER_CREATE, T_NAMESPACE_SEPARATOR, T_STR_OR_NONE, sep); + } + Object internDict; + if (intern == PNone.NO_VALUE) { + internDict = PFactory.createDict(PythonLanguage.get(inliningTarget)); + } else if (intern == PNone.NONE) { + internDict = PNone.NONE; + } else if (intern instanceof PDict) { + internDict = intern; + } else { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.INTERN_MUST_BE_A_DICTIONARY); + } + return createParserNode.execute(inliningTarget, encoding, sep, internDict); + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/XMLParserBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/XMLParserBuiltins.java new file mode 100644 index 0000000000..809860dfd1 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/pyexpat/XMLParserBuiltins.java @@ -0,0 +1,1537 @@ +/* + * 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.builtins.modules.pyexpat; + +import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; +import static com.oracle.graal.python.nodes.StringLiterals.T_READ; +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.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.XMLConstants; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLReader; +import org.xml.sax.ext.Attributes2; +import org.xml.sax.ext.DefaultHandler2; +import org.xml.sax.ext.EntityResolver2; +import org.xml.sax.helpers.DefaultHandler; + +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.builtins.CoreFunctions; +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.buffer.PythonBufferAccessLibrary; +import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAcquireLibrary; +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.type.TpSlots; +import com.oracle.graal.python.lib.PyLongAsLongNode; +import com.oracle.graal.python.lib.PyLongCheckNode; +import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; +import com.oracle.graal.python.lib.PyObjectIsTrueNode; +import com.oracle.graal.python.lib.PyUnicodeCheckNode; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.call.CallNode; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; +import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.PythonBinaryClinicBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.PythonTernaryClinicBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; +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.exception.PException; +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.GenerateCached; +import com.oracle.truffle.api.dsl.GenerateInline; +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.InternalByteArray; +import com.oracle.truffle.api.strings.TruffleString; + +@CoreFunctions(extendClasses = PythonBuiltinClassType.XMLParser) +public final class XMLParserBuiltins extends PythonBuiltins { + public static final TpSlots SLOTS = XMLParserBuiltinsSlotsGen.SLOTS; + + /* + * JAXP exposes entity-expansion protection through separate per-parser properties. The matching jdk.xml.* system + * properties are read once into static limit values so stricter process-wide limits remain effective without + * re-reading properties for every parse. The SAX feature URIs below disable external entity and DTD loading where + * the selected SAX implementation supports them. + */ + private static final int ENTITY_EXPANSION_LIMIT = 1_000_000; + private static final int TOTAL_ENTITY_SIZE_LIMIT = 1_000_000; + private static final int ENTITY_REPLACEMENT_LIMIT = 1_000_000; + private static final String JDK_ENTITY_EXPANSION_LIMIT_PROPERTY = "jdk.xml.entityExpansionLimit"; + private static final String JDK_TOTAL_ENTITY_SIZE_LIMIT_PROPERTY = "jdk.xml.totalEntitySizeLimit"; + private static final String JDK_ENTITY_REPLACEMENT_LIMIT_PROPERTY = "jdk.xml.entityReplacementLimit"; + private static final String JAXP_ENTITY_EXPANSION_LIMIT = "http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit"; + private static final String JAXP_TOTAL_ENTITY_SIZE_LIMIT = "http://www.oracle.com/xml/jaxp/properties/totalEntitySizeLimit"; + private static final String JAXP_ENTITY_REPLACEMENT_LIMIT = "http://www.oracle.com/xml/jaxp/properties/entityReplacementLimit"; + private static final String ENTITY_EXPANSION_LIMIT_VALUE = getParserLimit(JDK_ENTITY_EXPANSION_LIMIT_PROPERTY, ENTITY_EXPANSION_LIMIT); + private static final String TOTAL_ENTITY_SIZE_LIMIT_VALUE = getParserLimit(JDK_TOTAL_ENTITY_SIZE_LIMIT_PROPERTY, TOTAL_ENTITY_SIZE_LIMIT); + private static final String ENTITY_REPLACEMENT_LIMIT_VALUE = getParserLimit(JDK_ENTITY_REPLACEMENT_LIMIT_PROPERTY, ENTITY_REPLACEMENT_LIMIT); + private static final String SAX_EXTERNAL_GENERAL_ENTITIES = "http://xml.org/sax/features/external-general-entities"; + private static final String SAX_EXTERNAL_PARAMETER_ENTITIES = "http://xml.org/sax/features/external-parameter-entities"; + private static final String SAX_LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; + + @Override + protected List> getNodeFactories() { + return XMLParserBuiltinsFactory.getFactories(); + } + + @GenerateInline + @GenerateCached(false) + abstract static class CreateParserNode extends Node { + abstract Object execute(Node inliningTarget, Object encodingObj, Object namespaceSeparatorObj, Object intern); + + @Specialization + static Object doIt(Node inliningTarget, Object encodingObj, Object namespaceSeparatorObj, Object intern, + @Cached PyUnicodeCheckNode unicodeCheckNode, + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached TruffleString.CodePointLengthNode codePointLengthNode, + @Cached PRaiseNode raiseNode) { + TruffleString encoding = null; + if (!(encodingObj instanceof PNone)) { + if (!unicodeCheckNode.execute(inliningTarget, encodingObj)) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.ENCODING_NAME_MUST_BE_A_STRING); + } + encoding = castToTruffleStringNode.castKnownString(inliningTarget, encodingObj); + } + TruffleString sep = null; + if (!(namespaceSeparatorObj instanceof PNone)) { + if (!unicodeCheckNode.execute(inliningTarget, namespaceSeparatorObj)) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.NAMESPACE_SEPARATOR_MUST_BE); + } + TruffleString namespaceSeparator = castToTruffleStringNode.castKnownString(inliningTarget, namespaceSeparatorObj); + int len = codePointLengthNode.execute(namespaceSeparator, TS_ENCODING); + if (len > 1) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.NAMESPACE_SEPARATOR_MUST_BE); + } + sep = namespaceSeparator; + } + PythonLanguage language = PythonLanguage.get(inliningTarget); + PXMLParser parser = PFactory.createXMLParser(PythonBuiltinClassType.XMLParser, PythonBuiltinClassType.XMLParser.getInstanceShape(language), sep); + parser.setEncoding(encoding); + parser.setIntern(intern); + return parser; + } + } + + @Slot(value = SlotKind.tp_new, isComplex = true) + @SlotSignature(name = "xmlparser", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 3) + @GenerateNodeFactory + abstract static class NewNode extends PythonTernaryBuiltinNode { + @Specialization + static Object doIt(@SuppressWarnings("unused") Object cls, @SuppressWarnings("unused") Object arg1, Object arg2, + @Bind Node inliningTarget, + @Cached CreateParserNode createParserNode) { + return createParserNode.execute(inliningTarget, PNone.NONE, arg2 == PNone.NO_VALUE ? PNone.NONE : arg2, PNone.NONE); + } + } + + @Builtin(name = "buffer_text", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class BufferTextNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static boolean get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.isBufferText(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(VirtualFrame frame, PXMLParser self, Object value, + @Cached PyObjectIsTrueNode isTrueNode) { + self.setBufferText(isTrueNode.execute(frame, value)); + self.setBufferUsed(0); + return PNone.NONE; + } + } + + @Builtin(name = "namespace_prefixes", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class NamespacePrefixesNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static boolean get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.isNamespacePrefixes(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(VirtualFrame frame, PXMLParser self, Object value, + @Cached PyObjectIsTrueNode isTrueNode) { + self.setNamespacePrefixes(isTrueNode.execute(frame, value)); + return PNone.NONE; + } + } + + @Builtin(name = "ordered_attributes", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class OrderedAttributesNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static boolean get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.isOrderedAttributes(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(VirtualFrame frame, PXMLParser self, Object value, + @Cached PyObjectIsTrueNode isTrueNode) { + self.setOrderedAttributes(isTrueNode.execute(frame, value)); + return PNone.NONE; + } + } + + @Builtin(name = "specified_attributes", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class SpecifiedAttributesNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static boolean get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.isSpecifiedAttributes(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(VirtualFrame frame, PXMLParser self, Object value, + @Cached PyObjectIsTrueNode isTrueNode) { + self.setSpecifiedAttributes(isTrueNode.execute(frame, value)); + return PNone.NONE; + } + } + + @Builtin(name = "buffer_size", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class BufferSizeNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static int get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getBufferSize(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(VirtualFrame frame, PXMLParser self, Object value, + @Bind Node inliningTarget, + @Cached PyLongCheckNode longCheckNode, + @Cached PyLongAsLongNode asLongNode) { + if (!longCheckNode.execute(inliningTarget, value)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.MUST_BE_INTEGER, "buffer_size"); + } + long newBufferSize = asLongNode.execute(frame, inliningTarget, value); + if (newBufferSize <= 0) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.S_MUST_BE_GREATER_THAN_ZERO, "buffer_size"); + } + if (newBufferSize != (int) newBufferSize) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.OverflowError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, value); + } + self.setBufferSize((int) newBufferSize); + return PNone.NONE; + } + } + + @Builtin(name = "buffer_used", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class BufferUsedNode extends PythonUnaryBuiltinNode { + @Specialization + static int get(PXMLParser self) { + return self.getBufferUsed(); + } + } + + @Builtin(name = "CurrentByteIndex", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class CurrentByteIndexNode extends PythonUnaryBuiltinNode { + @Specialization + static int get(PXMLParser self) { + return self.getCurrentByteIndex(); + } + } + + @Builtin(name = "CurrentLineNumber", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class CurrentLineNumberNode extends PythonUnaryBuiltinNode { + @Specialization + static int get(PXMLParser self) { + return self.getCurrentLineNumber(); + } + } + + @Builtin(name = "CurrentColumnNumber", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class CurrentColumnNumberNode extends PythonUnaryBuiltinNode { + @Specialization + static int get(PXMLParser self) { + return self.getCurrentColumnNumber(); + } + } + + @Builtin(name = "ErrorByteIndex", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class ErrorByteIndexNode extends PythonUnaryBuiltinNode { + @Specialization + static int get(PXMLParser self) { + return self.getErrorByteIndex(); + } + } + + @Builtin(name = "ErrorLineNumber", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class ErrorLineNumberNode extends PythonUnaryBuiltinNode { + @Specialization + static int get(PXMLParser self) { + return self.getErrorLineNumber(); + } + } + + @Builtin(name = "ErrorColumnNumber", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class ErrorColumnNumberNode extends PythonUnaryBuiltinNode { + @Specialization + static int get(PXMLParser self) { + return self.getErrorColumnNumber(); + } + } + + @Builtin(name = "intern", minNumOfPositionalArgs = 1, isGetter = true) + @GenerateNodeFactory + abstract static class InternNode extends PythonUnaryBuiltinNode { + @Specialization + static Object get(PXMLParser self) { + return self.getIntern(); + } + } + + @Builtin(name = "StartElementHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class StartElementHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getStartElementHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setStartElementHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "EndElementHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class EndElementHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getEndElementHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setEndElementHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "ProcessingInstructionHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class ProcessingInstructionHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getProcessingInstructionHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setProcessingInstructionHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "CharacterDataHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class CharacterDataHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getCharacterDataHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setCharacterDataHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "UnparsedEntityDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class UnparsedEntityDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getUnparsedEntityDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setUnparsedEntityDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "NotationDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class NotationDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getNotationDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setNotationDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "StartNamespaceDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class StartNamespaceDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getStartNamespaceDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setStartNamespaceDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "EndNamespaceDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class EndNamespaceDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getEndNamespaceDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setEndNamespaceDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "CommentHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class CommentHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getCommentHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setCommentHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "StartCdataSectionHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class StartCdataSectionHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getStartCdataSectionHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setStartCdataSectionHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "EndCdataSectionHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class EndCdataSectionHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getEndCdataSectionHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setEndCdataSectionHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "DefaultHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class DefaultHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getDefaultHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setDefaultHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "DefaultHandlerExpand", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class DefaultHandlerExpandNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getDefaultHandlerExpand(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setDefaultHandlerExpand(value); + return PNone.NONE; + } + } + + @Builtin(name = "NotStandaloneHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class NotStandaloneHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getNotStandaloneHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setNotStandaloneHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "ExternalEntityRefHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class ExternalEntityRefHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getExternalEntityRefHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setExternalEntityRefHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "StartDoctypeDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class StartDoctypeDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getStartDoctypeDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setStartDoctypeDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "EndDoctypeDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class EndDoctypeDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getEndDoctypeDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setEndDoctypeDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "EntityDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class EntityDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getEntityDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setEntityDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "XmlDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class XmlDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getXmlDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setXmlDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "ElementDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class ElementDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getElementDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setElementDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "AttlistDeclHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class AttlistDeclHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getAttlistDeclHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setAttlistDeclHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "SkippedEntityHandler", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true) + @GenerateNodeFactory + abstract static class SkippedEntityHandlerNode extends PythonBinaryBuiltinNode { + @Specialization(guards = "isNoValue(value)") + static Object get(PXMLParser self, @SuppressWarnings("unused") PNone value) { + return self.getSkippedEntityHandler(); + } + + @Specialization(guards = "!isNoValue(value)") + static Object set(PXMLParser self, Object value) { + self.setSkippedEntityHandler(value); + return PNone.NONE; + } + } + + @Builtin(name = "Parse", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 3, parameterNames = {"$cls", "data", "isfinal"}) + @ArgumentClinic(name = "isfinal", conversion = ArgumentClinic.ClinicConversion.Boolean, defaultValue = "false") + @GenerateNodeFactory + abstract static class ParseNode extends PythonTernaryClinicBuiltinNode { + @Specialization + int parse(VirtualFrame frame, PXMLParser self, Object data, boolean isFinal, + @Cached("createFor($node)") BoundaryCallData boundaryCallData) { + Object savedState = BoundaryCallContext.enter(frame, boundaryCallData); + try { + doParse(self, data, isFinal); + } finally { + BoundaryCallContext.exit(frame, boundaryCallData, savedState); + } + return 1; + } + + @TruffleBoundary + private void doParse(PXMLParser self, Object data, boolean isFinal) { + if (self.isFinished()) { + throw raiseExpatError(this, ErrorMessages.PARSING_FINISHED, PXMLParser.XML_ERROR_FINISHED, 0, 1, 0); + } + if (isFinal && self.getData().length == 0 && PyUnicodeCheckNode.executeUncached(data)) { + TruffleString strData = CastToTruffleStringNode.castKnownStringUncached(data); + String javaData = strData.toJavaStringUncached(); + byte[] prologBytes = javaData.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1); + parseNow(self, false, new InputSource(new StringReader(javaData)), javaData::length, + detectXmlDecl(prologBytes), detectDoctypeHasInternalSubset(prologBytes)); + self.setFinished(true); + return; + } + appendData(self, data); + parseNow(self, !isFinal); + if (isFinal) { + self.setFinished(true); + } + } + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return XMLParserBuiltinsClinicProviders.ParseNodeClinicProviderGen.INSTANCE; + } + } + + @Builtin(name = "ParseFile", minNumOfPositionalArgs = 2) + @GenerateNodeFactory + abstract static class ParseFileNode extends PythonBinaryBuiltinNode { + @Specialization + int parseFile(VirtualFrame frame, PXMLParser self, Object file, + @Cached("createFor($node)") BoundaryCallData boundaryCallData) { + Object savedState = BoundaryCallContext.enter(frame, boundaryCallData); + try { + doParseFile(self, file); + } finally { + BoundaryCallContext.exit(frame, boundaryCallData, savedState); + } + self.setFinished(true); + return 1; + } + + @TruffleBoundary + private void doParseFile(PXMLParser self, Object file) { + if (self.isFinished()) { + throw raiseExpatError(this, ErrorMessages.PARSING_FINISHED, PXMLParser.XML_ERROR_FINISHED, 0, 1, 0); + } + PythonFileInputStream stream = new PythonFileInputStream(file, self.getBufferSize()); + stream.prefetch(); + byte[] prologBytes = stream.getPrologBytes(); + InputSource source = new InputSource(stream); + applyParserEncoding(self, source, detectXmlDecl(prologBytes)); + parseNow(self, false, source, stream::getBytesRead, detectXmlDecl(prologBytes), detectDoctypeHasInternalSubset(prologBytes)); + } + } + + @Builtin(name = "SetParamEntityParsing", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, parameterNames = {"$self", "flag"}) + @ArgumentClinic(name = "flag", conversion = ArgumentClinic.ClinicConversion.Int) + @GenerateNodeFactory + abstract static class SetParamEntityParsingNode extends PythonBinaryClinicBuiltinNode { + @Specialization + static int set(PXMLParser self, int value) { + self.setParamEntityParsing(value); + return 1; + } + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return XMLParserBuiltinsClinicProviders.SetParamEntityParsingNodeClinicProviderGen.INSTANCE; + } + } + + @Builtin(name = "UseForeignDTD", minNumOfPositionalArgs = 1, numOfPositionalOnlyArgs = 2, parameterNames = {"$cls", "flag"}) + @ArgumentClinic(name = "flag", conversion = ArgumentClinic.ClinicConversion.Boolean, defaultValue = "true") + @GenerateNodeFactory + abstract static class UseForeignDTDNode extends PythonBinaryClinicBuiltinNode { + @Specialization + static PNone set(PXMLParser self, boolean flag) { + self.setForeignDTD(flag); + return PNone.NONE; + } + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return XMLParserBuiltinsClinicProviders.UseForeignDTDNodeClinicProviderGen.INSTANCE; + } + } + + @Builtin(name = "GetReparseDeferralEnabled", minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class GetReparseDeferralEnabledNode extends PythonUnaryBuiltinNode { + @Specialization + static boolean get(PXMLParser self) { + return self.isReparseDeferralEnabled(); + } + } + + @Builtin(name = "SetReparseDeferralEnabled", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, parameterNames = {"$self", "enabled"}) + @ArgumentClinic(name = "enabled", conversion = ArgumentClinic.ClinicConversion.Boolean) + @GenerateNodeFactory + abstract static class SetReparseDeferralEnabledNode extends PythonBinaryClinicBuiltinNode { + @Specialization + static PNone set(PXMLParser self, boolean value) { + self.setReparseDeferralEnabled(value); + return PNone.NONE; + } + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return XMLParserBuiltinsClinicProviders.SetReparseDeferralEnabledNodeClinicProviderGen.INSTANCE; + } + } + + @Builtin(name = "SetBase", minNumOfPositionalArgs = 2, numOfPositionalOnlyArgs = 2, parameterNames = {"$self", "base"}) + @ArgumentClinic(name = "base", conversion = ArgumentClinic.ClinicConversion.TString) + @GenerateNodeFactory + abstract static class SetBaseNode extends PythonBinaryClinicBuiltinNode { + @Specialization + static PNone set(PXMLParser self, TruffleString base) { + self.setBase(base); + return PNone.NONE; + } + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return XMLParserBuiltinsClinicProviders.SetBaseNodeClinicProviderGen.INSTANCE; + } + } + + @Builtin(name = "GetBase", minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class GetBaseNode extends PythonUnaryBuiltinNode { + @Specialization + static Object get(PXMLParser self) { + return self.getBase() == null ? PNone.NONE : self.getBase(); + } + } + + @Builtin(name = "ExternalEntityParserCreate", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 3) + @GenerateNodeFactory + abstract static class ExternalEntityParserCreateNode extends PythonTernaryBuiltinNode { + @Specialization + Object create(PXMLParser self, @SuppressWarnings("unused") Object context, @SuppressWarnings("unused") Object encoding, + @Bind Node inliningTarget, + @Cached CreateParserNode createParserNode) { + return createParserNode.execute(inliningTarget, encoding == PNone.NO_VALUE ? PNone.NONE : encoding, self.getNamespaceSeparator() == null ? PNone.NONE : self.getNamespaceSeparator(), + self.getIntern()); + } + } + + @TruffleBoundary + private static int appendData(PXMLParser parser, Object data) { + byte[] existing = parser.getData(); + int existingLen = existing.length; + byte[] chunk = asByteArray(data); + int chunkLen = chunk.length; + if (chunkLen == 0) { + return 0; + } + byte[] merged = Arrays.copyOf(existing, existingLen + chunkLen); + System.arraycopy(chunk, 0, merged, existingLen, chunkLen); + parser.setData(merged); + return chunkLen; + } + + @TruffleBoundary + private static byte[] asByteArray(Object data) { + if (PyUnicodeCheckNode.executeUncached(data)) { + TruffleString utf8 = CastToTruffleStringNode.castKnownStringUncached(data).switchEncodingUncached(TruffleString.Encoding.UTF_8); + InternalByteArray internalByteArray = utf8.getInternalByteArrayUncached(TruffleString.Encoding.UTF_8); + int chunkLen = internalByteArray.getLength(); + if (chunkLen == 0) { + return EMPTY_BYTE_ARRAY; + } + return Arrays.copyOfRange(internalByteArray.getArray(), internalByteArray.getOffset(), internalByteArray.getOffset() + chunkLen); + } + Object buffer = PythonBufferAcquireLibrary.getUncached().acquireReadonly(data); + try { + PythonBufferAccessLibrary accessLib = PythonBufferAccessLibrary.getUncached(); + int chunkLen = accessLib.getBufferLength(buffer); + if (chunkLen == 0) { + return EMPTY_BYTE_ARRAY; + } + byte[] bytes = new byte[chunkLen]; + accessLib.readIntoByteArray(buffer, 0, bytes, 0, chunkLen); + return bytes; + } finally { + PythonBufferAccessLibrary.getUncached().release(buffer); + } + } + + @TruffleBoundary + private static void parseNow(PXMLParser parser, boolean swallowErrors) { + byte[] data = parser.getData(); + int byteLen = data.length; + XmlDeclInfo xmlDeclInfo = detectXmlDecl(data); + InputSource source = new InputSource(new ByteArrayInputStream(data)); + applyParserEncoding(parser, source, xmlDeclInfo); + parseNow(parser, swallowErrors, source, () -> byteLen, xmlDeclInfo, detectDoctypeHasInternalSubset(data)); + } + + @TruffleBoundary + private static void parseNow(PXMLParser parser, boolean swallowErrors, InputSource source, ByteIndexSupplier byteIndexSupplier) { + parseNow(parser, swallowErrors, source, byteIndexSupplier, null, false); + } + + @TruffleBoundary + private static void applyParserEncoding(PXMLParser parser, InputSource source, XmlDeclInfo xmlDeclInfo) { + TruffleString encoding = parser.getEncoding(); + if (encoding != null && (xmlDeclInfo == null || xmlDeclInfo.encoding == null)) { + source.setEncoding(encoding.toJavaStringUncached()); + } + } + + @TruffleBoundary + private static void parseNow(PXMLParser parser, boolean swallowErrors, InputSource source, ByteIndexSupplier byteIndexSupplier, XmlDeclInfo xmlDeclInfo, boolean doctypeHasInternalSubset) { + final class Handler extends DefaultHandler2 { + int line = 1; + int col; + int eventOrdinal; + Locator locator; + boolean keepCurrentPositionForNextCall; + final Map externalEntities = new HashMap<>(); + final Set resolvedExternalEntities = new HashSet<>(); + + @Override + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + Handler() { + if (parser.isForeignDTD() && parser.getParamEntityParsing() == PXMLParser.XML_PARAM_ENTITY_PARSING_ALWAYS) { + call(parser.getExternalEntityRefHandler(), PNone.NONE, PNone.NONE, PNone.NONE, PNone.NONE); + } + } + + @Override + public void startPrefixMapping(String prefix, String uri) { + call(parser.getStartNamespaceDeclHandler(), toTs(prefix), toTs(uri)); + } + + @Override + public void endPrefixMapping(String prefix) { + call(parser.getEndNamespaceDeclHandler(), toTs(prefix)); + } + + @Override + public void processingInstruction(String target, String data) { + call(parser.getProcessingInstructionHandler(), toTs(target), toTs(data)); + } + + @Override + public void startDocument() { + if (xmlDeclInfo != null) { + call(parser.getXmlDeclHandler(), toOptionalTs(xmlDeclInfo.version), toOptionalTs(xmlDeclInfo.encoding), xmlDeclInfo.standalone); + } + } + + @Override + public void startDTD(String name, String publicId, String systemId) { + if (xmlDeclInfo != null && xmlDeclInfo.standalone == 0) { + call(parser.getNotStandaloneHandler()); + } + call(parser.getStartDoctypeDeclHandler(), toTs(name), toTs(systemId), toOptionalTs(publicId), doctypeHasInternalSubset ? 1 : 0); + } + + @Override + public void endDTD() { + if (xmlDeclInfo != null && xmlDeclInfo.standalone == 0) { + call(parser.getNotStandaloneHandler()); + } + call(parser.getEndDoctypeDeclHandler()); + } + + @Override + public void internalEntityDecl(String name, String value) { + boolean isParameterEntity = name != null && name.startsWith("%"); + call(parser.getEntityDeclHandler(), toTs(name), isParameterEntity ? 1 : 0, toTs(value), parser.getBase() == null ? PNone.NONE : parser.getBase(), PNone.NONE, PNone.NONE, + PNone.NONE); + } + + @Override + public void externalEntityDecl(String name, String publicId, String systemId) { + boolean isParameterEntity = name != null && name.startsWith("%"); + if (!isParameterEntity) { + externalEntities.put(name, new ExternalEntityInfo(systemId, publicId)); + } + call(parser.getEntityDeclHandler(), toTs(name), isParameterEntity ? 1 : 0, PNone.NONE, parser.getBase() == null ? PNone.NONE : parser.getBase(), + toOptionalTs(normalizeSystemId(systemId)), + toOptionalTs(publicId), PNone.NONE); + } + + @Override + public void notationDecl(String name, String publicId, String systemId) { + call(parser.getNotationDeclHandler(), toTs(name), parser.getBase() == null ? PNone.NONE : parser.getBase(), toOptionalTs(normalizeSystemId(systemId)), toOptionalTs(publicId)); + } + + @Override + public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) { + call(parser.getUnparsedEntityDeclHandler(), toTs(name), parser.getBase() == null ? PNone.NONE : parser.getBase(), toOptionalTs(normalizeSystemId(systemId)), toOptionalTs(publicId), + toTs(notationName)); + } + + @Override + public void elementDecl(String name, String model) { + call(parser.getElementDeclHandler(), toTs(name), elementModel(model)); + } + + @Override + public void attributeDecl(String eName, String aName, String type, String mode, String value) { + Object defaultValue = toOptionalTs(value); + int required = 0; + if ("#REQUIRED".equals(mode)) { + defaultValue = PNone.NONE; + required = 1; + } else if ("#IMPLIED".equals(mode)) { + defaultValue = PNone.NONE; + required = 0; + } + call(parser.getAttlistDeclHandler(), toTs(eName), toTs(aName), toTs(type), defaultValue, required); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attrs) { + Object attrsObj; + int attrCount = attrs.getLength(); + if (parser.isSpecifiedAttributes() && attrs instanceof Attributes2 attrs2) { + attrCount = 0; + for (int i = 0; i < attrs.getLength(); i++) { + if (attrs2.isSpecified(i)) { + attrCount++; + } + } + } + if (parser.isOrderedAttributes()) { + List l = new ArrayList<>(attrCount * 2); + for (int i = 0; i < attrs.getLength(); i++) { + if (parser.isSpecifiedAttributes() && attrs instanceof Attributes2 attrs2 && !attrs2.isSpecified(i)) { + continue; + } + l.add(toTs(attributeName(attrs, i))); + l.add(toTs(attrs.getValue(i))); + } + attrsObj = PFactory.createList(PythonLanguage.get(null), l.toArray()); + } else { + PDict d = PFactory.createDict(PythonLanguage.get(null)); + for (int i = 0; i < attrs.getLength(); i++) { + if (parser.isSpecifiedAttributes() && attrs instanceof Attributes2 attrs2 && !attrs2.isSpecified(i)) { + continue; + } + d.setItem(toTs(attributeName(attrs, i)), toTs(attrs.getValue(i))); + } + attrsObj = d; + } + call(parser.getStartElementHandler(), toTs(elementName(uri, localName, qName)), attrsObj); + } + + @Override + public void endElement(String uri, String localName, String qName) { + call(parser.getEndElementHandler(), toTs(elementName(uri, localName, qName))); + } + + @Override + public void characters(char[] ch, int start, int length) { + if (length > 0) { + call(parser.getCharacterDataHandler(), toTs(new String(ch, start, length))); + } + } + + @Override + public void comment(char[] ch, int start, int length) { + call(parser.getCommentHandler(), toTs(new String(ch, start, length))); + } + + @Override + public void startCDATA() { + call(parser.getStartCdataSectionHandler()); + } + + @Override + public void endCDATA() { + call(parser.getEndCdataSectionHandler()); + } + + @Override + public void skippedEntity(String name) { + ExternalEntityInfo externalEntity = externalEntities.get(name); + if (externalEntity != null && !resolvedExternalEntities.contains(name)) { + call(parser.getExternalEntityRefHandler(), PNone.NONE, parser.getBase() == null ? PNone.NONE : parser.getBase(), + toOptionalTs(normalizeSystemId(externalEntity.systemId)), toOptionalTs(externalEntity.publicId)); + return; + } + call(parser.getSkippedEntityHandler(), toTs(name), 0); + if (locator != null) { + int entityLen = name.length() + 2; // '&' + ';' + line = Math.max(1, locator.getLineNumber()); + col = Math.max(0, locator.getColumnNumber() - 1 - entityLen); + parser.setCurrentLineNumber(line); + parser.setCurrentColumnNumber(col); + parser.setErrorLineNumber(line); + parser.setErrorColumnNumber(col); + } + keepCurrentPositionForNextCall = true; + call(parser.getDefaultHandlerExpand(), toTs("&" + name + ";")); + } + + private String elementName(String uri, String localName, String qName) { + if (parser.getNamespaceSeparator() != null && uri != null && !uri.isEmpty()) { + String sep = parser.getNamespaceSeparator().toJavaStringUncached(); + if (parser.isNamespacePrefixes() && qName != null && !qName.isEmpty()) { + int colon = qName.indexOf(':'); + if (colon > 0) { + String prefix = qName.substring(0, colon); + return uri + sep + localName + sep + prefix; + } + } + return uri + sep + localName; + } + return qName == null || qName.isEmpty() ? localName : qName; + } + + private String attributeName(Attributes attrs, int i) { + String uri = attrs.getURI(i); + String localName = attrs.getLocalName(i); + String qName = attrs.getQName(i); + if (parser.getNamespaceSeparator() != null && uri != null && !uri.isEmpty()) { + String sep = parser.getNamespaceSeparator().toJavaStringUncached(); + if (parser.isNamespacePrefixes() && qName != null && !qName.isEmpty()) { + int colon = qName.indexOf(':'); + if (colon > 0) { + String prefix = qName.substring(0, colon); + return uri + sep + localName + sep + prefix; + } + } + return uri + sep + localName; + } + return qName == null || qName.isEmpty() ? localName : qName; + } + + private void call(Object handler, Object... args) { + boolean shouldDeliver = eventOrdinal++ >= parser.getDeliveredEventCount(); + if (!shouldDeliver) { + return; + } + if (!keepCurrentPositionForNextCall && locator != null) { + line = Math.max(1, locator.getLineNumber()); + col = Math.max(0, locator.getColumnNumber() - 1); + parser.setCurrentLineNumber(line); + parser.setCurrentColumnNumber(col); + parser.setErrorLineNumber(line); + parser.setErrorColumnNumber(col); + } + keepCurrentPositionForNextCall = false; + if (handler != PNone.NONE) { + CallNode.executeUncached(handler, args); + } + } + + private TruffleString toTs(String s) { + return s == null ? T_EMPTY_STRING : toTruffleStringUncached(s); + } + + private Object toOptionalTs(String s) { + return s == null || s.isEmpty() ? PNone.NONE : toTruffleStringUncached(s); + } + + private String normalizeSystemId(String systemId) { + if (systemId == null || parser.getBase() != null) { + return systemId; + } + String s = systemId; + while (s.startsWith("file://")) { + s = s.substring("file://".length()); + } + if (s.startsWith("/")) { + int idx = s.lastIndexOf('/'); + if (idx >= 0 && idx + 1 < s.length()) { + return s.substring(idx + 1); + } + } + return systemId; + } + + private Object elementModel(String model) { + if ("ANY".equals(model)) { + return PFactory.createTuple(PythonLanguage.get(null), new Object[]{2, 0, PNone.NONE, + PFactory.createTuple(PythonLanguage.get(null), EMPTY_OBJECT_ARRAY)}); + } + return toTs(model); + } + } + + Handler handler = new Handler(); + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(parser.getNamespaceSeparator() != null); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + SAXParser saxParser = factory.newSAXParser(); + saxParser.setProperty(JAXP_ENTITY_EXPANSION_LIMIT, ENTITY_EXPANSION_LIMIT_VALUE); + saxParser.setProperty(JAXP_TOTAL_ENTITY_SIZE_LIMIT, TOTAL_ENTITY_SIZE_LIMIT_VALUE); + saxParser.setProperty(JAXP_ENTITY_REPLACEMENT_LIMIT, ENTITY_REPLACEMENT_LIMIT_VALUE); + saxParser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + XMLReader reader = saxParser.getXMLReader(); + setFeatureIfSupported(reader, SAX_EXTERNAL_GENERAL_ENTITIES, false); + setFeatureIfSupported(reader, SAX_EXTERNAL_PARAMETER_ENTITIES, false); + setFeatureIfSupported(reader, SAX_LOAD_EXTERNAL_DTD, false); + reader.setEntityResolver(new EntityResolver2() { + @Override + public InputSource getExternalSubset(String name, String baseURI) { + return null; + } + + @Override + public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) { + if (name != null && !name.isEmpty()) { + handler.resolvedExternalEntities.add(name); + } + handler.call(parser.getExternalEntityRefHandler(), PNone.NONE, parser.getBase() == null ? PNone.NONE : parser.getBase(), + handler.toOptionalTs(handler.normalizeSystemId(systemId)), handler.toOptionalTs(publicId)); + return new InputSource(new StringReader("")); + } + + @Override + public InputSource resolveEntity(String publicId, String systemId) { + return new InputSource(new StringReader("")); + } + }); + reader.setContentHandler(handler); + reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler); + reader.setProperty("http://xml.org/sax/properties/declaration-handler", handler); + reader.setDTDHandler(handler); + reader.setErrorHandler(new DefaultHandler()); + reader.parse(source); + int byteIndex = byteIndexSupplier.get(); + parser.setDeliveredEventCount(handler.eventOrdinal); + parser.setCurrentByteIndex(byteIndex); + parser.setCurrentLineNumber(handler.line); + parser.setCurrentColumnNumber(handler.col); + parser.setErrorByteIndex(parser.getCurrentByteIndex()); + parser.setErrorLineNumber(parser.getCurrentLineNumber()); + parser.setErrorColumnNumber(parser.getCurrentColumnNumber()); + } catch (SAXParseException e) { + parser.setCurrentByteIndex(byteIndexSupplier.get()); + parser.setDeliveredEventCount(handler.eventOrdinal); + parser.setCurrentLineNumber(e.getLineNumber()); + parser.setCurrentColumnNumber(Math.max(0, e.getColumnNumber() - 1)); + parser.setErrorByteIndex(parser.getCurrentByteIndex()); + parser.setErrorLineNumber(parser.getCurrentLineNumber()); + parser.setErrorColumnNumber(parser.getCurrentColumnNumber()); + if (!swallowErrors) { + TruffleString msg = toTruffleStringUncached(formatErrorMessage(e)); + throw raiseExpatError(null, msg, mapErrorCode(e), parser.getCurrentByteIndex(), parser.getCurrentLineNumber(), + parser.getCurrentColumnNumber()); + } + } catch (PException e) { + throw e; + } catch (Exception e) { + parser.setCurrentByteIndex(byteIndexSupplier.get()); + parser.setDeliveredEventCount(handler.eventOrdinal); + parser.setErrorByteIndex(parser.getCurrentByteIndex()); + parser.setErrorLineNumber(parser.getCurrentLineNumber()); + parser.setErrorColumnNumber(parser.getCurrentColumnNumber()); + if (!swallowErrors) { + TruffleString msg = toTruffleStringUncached(e.getMessage() == null ? "unclosed token" : e.getMessage()); + throw raiseExpatError(null, msg, PXMLParser.XML_ERROR_UNCLOSED_TOKEN, parser.getCurrentByteIndex(), + parser.getCurrentLineNumber(), + parser.getCurrentColumnNumber()); + } + } + } + + private static String getParserLimit(String property, int defaultLimit) { + String configuredLimit = System.getProperty(property); + if (configuredLimit != null) { + try { + int limit = Integer.parseInt(configuredLimit); + if (limit > 0 && limit < defaultLimit) { + // Preserve a user-configured process-wide limit when it is stricter than GraalPy's default. + return configuredLimit; + } + } catch (NumberFormatException ignored) { + } + } + return Integer.toString(defaultLimit); + } + + private static void setFeatureIfSupported(XMLReader reader, String feature, boolean value) { + try { + reader.setFeature(feature, value); + } catch (Exception ignored) { + } + } + + @FunctionalInterface + private interface ByteIndexSupplier { + int get(); + } + + private static final class PythonFileInputStream extends InputStream { + private static final int PROLOG_CAPTURE_LIMIT = 4096; + + private final Object file; + private final int readSize; + private byte[] buffer = EMPTY_BYTE_ARRAY; + private int offset; + private boolean eof; + private int bytesRead; + private byte[] prologBytes = EMPTY_BYTE_ARRAY; + + private PythonFileInputStream(Object file, int readSize) { + this.file = file; + this.readSize = readSize; + } + + @Override + public int read() throws IOException { + byte[] b = new byte[1]; + int n = read(b, 0, 1); + return n == -1 ? -1 : b[0] & 0xFF; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (len == 0) { + return 0; + } + if (offset >= buffer.length) { + fillBuffer(); + if (eof) { + return -1; + } + } + int n = Math.min(len, buffer.length - offset); + System.arraycopy(buffer, offset, b, off, n); + offset += n; + return n; + } + + private void fillBuffer() throws IOException { + if (eof) { + return; + } + try { + Object chunkObj = PyObjectCallMethodObjArgs.executeUncached(file, T_READ, readSize); + byte[] chunk = asByteArray(chunkObj); + if (chunk.length == 0) { + eof = true; + buffer = EMPTY_BYTE_ARRAY; + offset = 0; + return; + } + buffer = chunk; + offset = 0; + bytesRead += chunk.length; + capturePrologBytes(chunk); + } catch (PException e) { + throw new IOException(e); + } + } + + int getBytesRead() { + return bytesRead; + } + + byte[] getPrologBytes() { + return prologBytes; + } + + void prefetch() { + if (buffer.length == 0 && !eof) { + try { + fillBuffer(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + private void capturePrologBytes(byte[] chunk) { + if (prologBytes.length >= PROLOG_CAPTURE_LIMIT) { + return; + } + int newLen = Math.min(PROLOG_CAPTURE_LIMIT, prologBytes.length + chunk.length); + byte[] merged = Arrays.copyOf(prologBytes, newLen); + int canCopy = newLen - prologBytes.length; + System.arraycopy(chunk, 0, merged, prologBytes.length, canCopy); + prologBytes = merged; + } + } + + private record ExternalEntityInfo(String systemId, String publicId) { + } + + private record XmlDeclInfo(String version, String encoding, int standalone) { + } + + @TruffleBoundary + private static boolean detectDoctypeHasInternalSubset(byte[] data) { + String text = new String(data, java.nio.charset.StandardCharsets.ISO_8859_1); + String doctypeStart = "') { + return false; + } + } + } + return false; + } + + @TruffleBoundary + private static XmlDeclInfo detectXmlDecl(byte[] data) { + if (data.length == 0 || data[0] != '<') { + return null; + } + int end = -1; + for (int i = 1; i < data.length; i++) { + if (data[i - 1] == '?' && data[i] == '>') { + end = i + 1; + break; + } + } + if (end == -1) { + return null; + } + String decl = new String(data, 0, end, java.nio.charset.StandardCharsets.ISO_8859_1); + if (!decl.startsWith("= decl.length()) { + return null; + } + char quote = decl.charAt(valueStart); + if (quote != '\'' && quote != '"') { + return null; + } + int end = decl.indexOf(quote, valueStart + 1); + if (end < 0) { + return null; + } + return decl.substring(valueStart + 1, end); + } + + private static PException raiseExpatError(Node raisingNode, TruffleString msg, int code, int byteIndex, int line, int column) { + PythonLanguage language = PythonLanguage.get(raisingNode); + PBaseException exc = PFactory.createBaseException(language, PythonBuiltinClassType.PyExpatError, msg, EMPTY_OBJECT_ARRAY); + exc.setAttribute(tsLiteral("code"), code); + exc.setAttribute(tsLiteral("lineno"), line); + exc.setAttribute(tsLiteral("offset"), column); + exc.setAttribute(tsLiteral("message"), msg); + exc.setAttribute(tsLiteral("byteindex"), byteIndex); + throw PRaiseNode.raiseExceptionObjectStatic(raisingNode, exc, false); + } + + private static String formatErrorMessage(SAXParseException e) { + String message = e.getMessage(); + if (message == null) { + return "syntax error"; + } + if (isEntityAmplificationLimitError(message)) { + return PXMLParser.XML_ERROR_AMPLIFICATION_LIMIT_BREACH_MESSAGE + ": line " + e.getLineNumber() + ", column " + Math.max(0, e.getColumnNumber() - 1); + } + if (message.contains("entity") && message.contains("not declared")) { + int firstQuote = message.indexOf('"'); + int secondQuote = firstQuote >= 0 ? message.indexOf('"', firstQuote + 1) : -1; + if (firstQuote >= 0 && secondQuote > firstQuote + 1) { + String entity = message.substring(firstQuote + 1, secondQuote); + return "undefined entity &" + entity + ";: line " + e.getLineNumber() + ", column " + Math.max(0, e.getColumnNumber() - 1); + } + } + return message; + } + + private static int mapErrorCode(SAXParseException e) { + String message = e.getMessage(); + if (message != null) { + if (isEntityAmplificationLimitError(message)) { + return PXMLParser.XML_ERROR_AMPLIFICATION_LIMIT_BREACH; + } + if (message.contains("start and end within the same entity") || message.contains("premature end of file") || message.contains("must be terminated")) { + return PXMLParser.XML_ERROR_UNCLOSED_TOKEN; + } + } + return PXMLParser.XML_ERROR_SYNTAX; + } + + private static boolean isEntityAmplificationLimitError(String message) { + return message.contains("JAXP00010001") || message.contains("JAXP00010004") || message.contains("JAXP00010007") || message.contains("entityExpansionLimit") || + message.contains("totalEntitySizeLimit") || message.contains("entityReplacementLimit"); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/MatchBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/MatchBuiltins.java index 519bb94d58..d166e20c21 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/MatchBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/MatchBuiltins.java @@ -595,7 +595,18 @@ static Object lastGroup(PMatch self, return PNone.NONE; } - for (Map.Entry entry : self.pattern.groupToIndexMap.entrySet()) { + String name = getLastGroupName(self.pattern.groupToIndexMap, groupIndex); + if (name != null) { + return fromJavaStringNode.execute(name, TS_ENCODING); + } + + // the last matched group does not have a name + return PNone.NONE; + } + + @TruffleBoundary + private static String getLastGroupName(LinkedHashMap groupToIndexMap, int groupIndex) { + for (Map.Entry entry : groupToIndexMap.entrySet()) { Object value = entry.getValue(); if (!(value instanceof Integer valueInteger)) { @@ -606,13 +617,10 @@ static Object lastGroup(PMatch self, if (valueInteger == groupIndex) { // a named group found - String name = entry.getKey(); - return fromJavaStringNode.execute(name, TS_ENCODING); + return entry.getKey(); } } - - // the last matched group does not have a name - return PNone.NONE; + return null; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternBuiltins.java index 2dc57a4264..2c11b8288f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternBuiltins.java @@ -89,7 +89,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare; import com.oracle.graal.python.lib.PyCallableCheckNode; -import com.oracle.graal.python.lib.PyObjectAsciiNode; +import com.oracle.graal.python.lib.PyObjectAsciiAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; @@ -1408,7 +1408,7 @@ static ParsedReplacement parseReplacement(Node inliningTarget, VirtualFrame fram if (!isIdentifierNode.execute(inliningTarget, name) || binary && !ascii) { errorProfile.enter(inliningTarget); throw raiseRegexErrorNode.executeFormatted(frame, BAD_CHAR_IN_GROUP_NAME, replacement, toCodepointIndex(nameStartPos, binary), - binary ? PyObjectAsciiNode.executeUncached(name) : PyObjectReprAsTruffleStringNode.executeUncached(name)); + binary ? PyObjectAsciiAsTruffleStringNode.executeUncached(name) : PyObjectReprAsTruffleStringNode.executeUncached(name)); } Object namedCaptureGroups = TRegexUtil.TRegexCompiledRegexAccessor.namedCaptureGroups(tregexCompiledRegex, inliningTarget, readNamedGroupsNode); if (!TRegexUtil.TRegexNamedCaptureGroupsAccessor.hasGroup(namedCaptureGroups, name, genericInteropLib)) { 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/re/SREModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/SREModuleBuiltins.java index 54773ad15d..c367ac7e35 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/SREModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/SREModuleBuiltins.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 @@ -158,9 +158,8 @@ protected ArgumentClinicProvider getArgumentClinic() { } @Specialization - @TruffleBoundary static boolean isCased(Object module, int codepoint) { - return codepoint < 128 && UCharacter.isLetter(codepoint); + return 'A' <= codepoint && codepoint <= 'Z' || 'a' <= codepoint && codepoint <= 'z'; } } @@ -175,13 +174,8 @@ protected ArgumentClinicProvider getArgumentClinic() { } @Specialization - @TruffleBoundary static int toLower(Object module, int codepoint) { - if (codepoint >= 128) { - return codepoint; - } - - return UCharacter.toLowerCase(codepoint); + return 'A' <= codepoint && codepoint <= 'Z' ? codepoint | 0x20 : codepoint; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/weakref/PProxyType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/weakref/PProxyType.java index f39653ccde..325cfeee11 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/weakref/PProxyType.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/weakref/PProxyType.java @@ -46,16 +46,13 @@ public class PProxyType extends PythonBuiltinObject { - public final Object object; public final PReferenceType weakReference; - public PProxyType(Object cls, Shape instanceShape, Object object, PReferenceType weakReference) { + public PProxyType(Object cls, Shape instanceShape, PReferenceType weakReference) { super(cls, instanceShape); - assert (object != null); assert (weakReference != null); - this.object = object; this.weakReference = weakReference; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/weakref/ProxyTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/weakref/ProxyTypeBuiltins.java index e21082fc96..f8c7da9632 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/weakref/ProxyTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/weakref/ProxyTypeBuiltins.java @@ -190,7 +190,7 @@ static PProxyType newNode(VirtualFrame frame, Node inliningTarget, Object object PythonBuiltinClassType cls = PythonBuiltinClassType.PProxyType; Shape shape = cls.getInstanceShape(language); - return new PProxyType(cls, shape, object, weakReference); + return new PProxyType(cls, shape, weakReference); } } @@ -268,9 +268,13 @@ static Object repr(PProxyType self, @Cached ObjectNodes.GetIdNode getIdNode, @Cached TypeNodes.GetQualNameNode getQualNameNode, @Cached CastToJavaStringNode castToJavaStringNode) { - Object object = unwrap(self, inliningTarget); + Object object = self.weakReference.getPyObject(); long selfId = (long) getIdNode.execute(self); + if (object == PNone.NONE) { + long noneId = (long) getIdNode.execute(PNone.NONE); + return reprBoundary(selfId, noneId, "NoneType"); + } long objectId = (long) getIdNode.execute(object); TruffleString objectTypeNameTS = getQualNameNode.execute(inliningTarget, ((PythonObject) object).getPythonClass()); String objectTypeName = castToJavaStringNode.execute(objectTypeNameTS); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/JavaCompress.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/JavaCompress.java index b486237667..288d955a3d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/JavaCompress.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/JavaCompress.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 @@ -183,9 +183,9 @@ private byte[] compressFinish() { @TruffleBoundary protected static byte[] compressFinish(byte[] bytes, int length, int level, int wbits, Node node) { CompressStream stream = createStream(level, wbits); - stream.deflater.setInput(bytes, 0, length); if (stream.stream != null) { try { + stream.stream.write(bytes, 0, length); stream.stream.finish(); return stream.out.toByteArray(); } catch (ZipException ze) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/JavaDecompress.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/JavaDecompress.java index b9752be544..2cfa6385a5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/JavaDecompress.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/JavaDecompress.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 @@ -42,18 +42,16 @@ import static com.oracle.graal.python.builtins.modules.zlib.ZLibModuleBuiltins.EMPTY_BYTE_ARRAY; import static com.oracle.graal.python.builtins.modules.zlib.ZLibModuleBuiltins.MAX_WBITS; +import static com.oracle.graal.python.builtins.modules.zlib.ZlibNodes.Z_DATA_ERROR; +import static com.oracle.graal.python.nodes.ErrorMessages.ERROR_D_S_S; import static com.oracle.graal.python.nodes.ErrorMessages.WHILE_SETTING_ZDICT; import static com.oracle.graal.python.runtime.exception.PythonErrorType.ZLibError; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; +import java.util.zip.CRC32; import java.util.zip.DataFormatException; -import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; -import java.util.zip.ZipException; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; @@ -61,7 +59,6 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.runtime.object.PFactory; 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.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; @@ -101,33 +98,42 @@ public void append(byte[] bytes, int off, int length) { public int length() { return count - pos; } - } - - private static class GZIPDecompressStream extends GZIPInputStream { - public GZIPDecompressStream(InputStream in) throws IOException { - super(in); + public int count() { + return count; } - public Inflater getInflater() { - return inf; + public int byteAt(int index) { + return buf[index] & 0xff; } - public void fillInput() throws IOException { - fill(); + public void updateCrc(CRC32 crc, int offset, int length) { + crc.update(buf, offset, length); } } private static class DecompressStream { Inflater inflater; - GZIPDecompressStream stream; + final boolean gzip; + int inputOffset; + final byte[] gzipTrailer; + final CRC32 gzipCrc; + int gzipTrailerLength; + int gzipUnusedLength; + boolean gzipTrailerComplete; protected final ZLibByteInputStream in; - DecompressStream(Inflater inflater) { + DecompressStream(Inflater inflater, boolean gzip) { this.inflater = inflater; - this.stream = null; + this.gzip = gzip; + this.inputOffset = 0; + this.gzipTrailer = gzip ? new byte[8] : null; + this.gzipCrc = gzip ? new CRC32() : null; + this.gzipTrailerLength = 0; + this.gzipUnusedLength = 0; + this.gzipTrailerComplete = !gzip; if (inflater == null) { this.in = new ZLibByteInputStream(new byte[HEADER_TRAILER_SIZE + 1], 0, 0); } else { @@ -136,15 +142,18 @@ private static class DecompressStream { } } + private static boolean isGZIP(int wbits) { + return wbits > (MAX_WBITS + 9) && wbits <= (MAX_WBITS + 16); + } + @TruffleBoundary private static DecompressStream createStream(int wbits) { - if (wbits > (MAX_WBITS + 9) && wbits <= (MAX_WBITS + 16)) { - // We delay the creation of a GZIP stream until we get an input; GZIPInputStream will - // check the data header during initialization. - return new DecompressStream(null); + if (isGZIP(wbits)) { + // Delay creating the raw inflater until the gzip header is complete. + return new DecompressStream(null, true); } else { Inflater inf = new Inflater(wbits < 0); - return new DecompressStream(inf); + return new DecompressStream(inf, false); } } @@ -155,41 +164,134 @@ public JavaDecompress(Object cls, Shape instanceShape, int wbits, byte[] zdict) this.stream = createStream(wbits); } + private static int gzipHeaderLength(ZLibByteInputStream in, boolean force, Node node) { + if (in.count() < HEADER_TRAILER_SIZE) { + return incompleteGZIPHeader(force, node); + } + if (in.byteAt(0) != 0x1f || in.byteAt(1) != 0x8b || in.byteAt(2) != 8 || (in.byteAt(3) & 0xe0) != 0) { + throw PRaiseNode.raiseStatic(node, ZLibError, ErrorMessages.WHILE_PREPARING_TO_S_DATA, "decompress"); + } + int flags = in.byteAt(3); + int offset = HEADER_TRAILER_SIZE; + if ((flags & 0x04) != 0) { + if (in.count() < offset + 2) { + return incompleteGZIPHeader(force, node); + } + int extraLength = in.byteAt(offset) | (in.byteAt(offset + 1) << 8); + offset += 2 + extraLength; + if (in.count() < offset) { + return incompleteGZIPHeader(force, node); + } + } + if ((flags & 0x08) != 0) { + offset = skipGZIPHeaderString(in, offset, force, node); + if (offset < 0) { + return offset; + } + } + if ((flags & 0x10) != 0) { + offset = skipGZIPHeaderString(in, offset, force, node); + if (offset < 0) { + return offset; + } + } + if ((flags & 0x02) != 0) { + int crcOffset = offset; + offset += 2; + if (in.count() < offset) { + return incompleteGZIPHeader(force, node); + } + validateGZIPHeaderCrc(in, crcOffset, node); + } + return offset; + } + + private static int skipGZIPHeaderString(ZLibByteInputStream in, int offset, boolean force, Node node) { + while (offset < in.count()) { + if (in.byteAt(offset++) == 0) { + return offset; + } + } + return incompleteGZIPHeader(force, node); + } + + private static int incompleteGZIPHeader(boolean force, Node node) { + if (force) { + throw PRaiseNode.raiseStatic(node, ZLibError, ErrorMessages.ERROR_5_WHILE_DECOMPRESSING); + } + return -1; + } + + private static void validateGZIPHeaderCrc(ZLibByteInputStream in, int crcOffset, Node node) { + CRC32 crc = new CRC32(); + in.updateCrc(crc, 0, crcOffset); + int expectedCrc = in.byteAt(crcOffset) | (in.byteAt(crcOffset + 1) << 8); + if (((int) crc.getValue() & 0xffff) != expectedCrc) { + throw PRaiseNode.raiseStatic(node, ZLibError, ERROR_D_S_S, Z_DATA_ERROR, + "while decompressing data", "header crc mismatch"); + } + } + private static boolean isGZIPStreamReady(DecompressStream stream, byte[] data, int length, boolean force, Node node) { assert !isReady(stream); + int oldCount = stream.in.count(); stream.in.append(data, 0, length); - try { - if (stream.in.length() > HEADER_TRAILER_SIZE || force) { - // GZIPInputStream will read the header during initialization - stream.stream = new GZIPDecompressStream(stream.in); - stream.inflater = stream.stream.getInflater(); - stream.stream.fillInput(); - return true; - } - } catch (ZipException ze) { - throw PRaiseNode.raiseStatic(node, ZLibError, ze); - } catch (IOException e) { - throw CompilerDirectives.shouldNotReachHere(e); + int headerLength = gzipHeaderLength(stream.in, force, node); + if (headerLength >= 0) { + stream.inflater = new Inflater(true); + stream.inputOffset = Math.max(0, headerLength - oldCount); + stream.inflater.setInput(data, stream.inputOffset, length - stream.inputOffset); + return true; } return false; } + private static long getLittleEndianUnsignedInt(byte[] data, int offset) { + return (data[offset] & 0xffL) | + ((data[offset + 1] & 0xffL) << 8) | + ((data[offset + 2] & 0xffL) << 16) | + ((data[offset + 3] & 0xffL) << 24); + } + + private static void validateGZIPTrailer(DecompressStream stream, Node node) { + assert stream.gzip; + long expectedCrc = getLittleEndianUnsignedInt(stream.gzipTrailer, 0); + long expectedSize = getLittleEndianUnsignedInt(stream.gzipTrailer, 4); + if (stream.gzipCrc.getValue() != expectedCrc) { + throw PRaiseNode.raiseStatic(node, ZLibError, ERROR_D_S_S, Z_DATA_ERROR, + "while decompressing data", "incorrect data check"); + } + if ((stream.inflater.getBytesWritten() & 0xffffffffL) != expectedSize) { + throw PRaiseNode.raiseStatic(node, ZLibError, ERROR_D_S_S, Z_DATA_ERROR, + "while decompressing data", "incorrect length check"); + } + } + + private static boolean consumeGZIPTrailer(DecompressStream stream, byte[] data, int offset, int length, Node node) { + if (!stream.gzip || (!stream.inflater.finished() && !stream.gzipTrailerComplete)) { + return false; + } + if (stream.gzipTrailerComplete) { + stream.gzipUnusedLength = length; + return true; + } + int trailerBytes = Math.min(stream.gzipTrailer.length - stream.gzipTrailerLength, length); + PythonUtils.arraycopy(data, offset, stream.gzipTrailer, stream.gzipTrailerLength, trailerBytes); + stream.gzipTrailerLength += trailerBytes; + stream.gzipUnusedLength = 0; + if (stream.gzipTrailerLength == stream.gzipTrailer.length) { + validateGZIPTrailer(stream, node); + stream.gzipTrailerComplete = true; + stream.gzipUnusedLength = length - trailerBytes; + } + return true; + } + private static boolean isGZIPStreamFinishing(DecompressStream stream, byte[] data, int length, Node node) { - if (stream.stream != null && stream.inflater.finished()) { - stream.in.append(data, 0, length); - try { - if (stream.in.length() >= HEADER_TRAILER_SIZE) { - stream.stream.fillInput(); - // this should trigger reading trailer - stream.stream.read(); - stream.stream = null; - } - return true; - } catch (ZipException ze) { - throw PRaiseNode.raiseStatic(node, ZLibError, ze); - } catch (IOException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } + if (stream.gzip && (stream.inflater.finished() || stream.gzipTrailerComplete)) { + consumeGZIPTrailer(stream, data, 0, length, node); + stream.inputOffset = 0; + return true; } return false; } @@ -206,10 +308,36 @@ private boolean setInflaterInput(byte[] data, int length, Node node) { return false; } } + stream.inputOffset = 0; stream.inflater.setInput(data, 0, length); return true; } + private static boolean needsReplay(DecompressStream stream, long bytesRead, long bytesWritten, boolean finishInflater) { + return stream.inflater.getBytesRead() < bytesRead || stream.inflater.getBytesWritten() < bytesWritten || + (finishInflater && !stream.inflater.finished()); + } + + private static void replayInflaterInput(JavaDecompress obj, long bytesRead, long bytesWritten, boolean finishInflater, boolean isRAW) + throws DataFormatException { + byte[] result = new byte[ZLibModuleBuiltins.DEF_BUF_SIZE]; + boolean zdictIsSet = isRAW; + while (needsReplay(obj.stream, bytesRead, bytesWritten, finishInflater) && !obj.stream.inflater.finished()) { + long remainingBytes = bytesWritten - obj.stream.inflater.getBytesWritten(); + int len = remainingBytes > 0 ? (int) Math.min(result.length, remainingBytes) : result.length; + int n = obj.stream.inflater.inflate(result, 0, len); + if (n == 0) { + if (!zdictIsSet && obj.stream.inflater.needsDictionary() && obj.getZdict().length > 0) { + obj.setDictionary(); + zdictIsSet = true; + continue; + } + break; + } + updateGZIPCrc(obj.stream, result, n); + } + } + @TruffleBoundary protected JavaDecompress copy(Node node) { assert canCopy(); @@ -223,10 +351,12 @@ protected JavaDecompress copy(Node node) { if (!obj.setInflaterInput(inputData, inputLen, node)) { return obj; } - int n = obj.stream.inflater.inflate(new byte[ZLibModuleBuiltins.DEF_BUF_SIZE]); - if (!isRAW && n == 0 && obj.stream.inflater.needsDictionary() && getZdict().length > 0) { - obj.setDictionary(); - obj.stream.inflater.inflate(new byte[ZLibModuleBuiltins.DEF_BUF_SIZE]); + replayInflaterInput(obj, stream.inflater.getBytesRead(), stream.inflater.getBytesWritten(), stream.inflater.finished(), + isRAW); + if (stream.gzip && stream.inflater.finished() && obj.stream.inflater.finished()) { + int remaining = obj.stream.inflater.getRemaining(); + int remainingOffset = obj.stream.inputOffset + (inputLen - obj.stream.inputOffset - remaining); + consumeGZIPTrailer(obj.stream, inputData, remainingOffset, remaining, node); } } catch (DataFormatException e) { // pass @@ -234,6 +364,7 @@ protected JavaDecompress copy(Node node) { } obj.setUnconsumedTail(getUnconsumedTail()); obj.setUnusedData(getUnusedData()); + obj.setEof(isEof()); return obj; } @@ -251,16 +382,7 @@ private byte[] createByteArray(byte[] bytes, int length, int maxLength, int bufS boolean zdictIsSet = false; while (baos.size() < maxLen && !stream.inflater.finished()) { if (stream.inflater.needsInput()) { - if (stream.stream == null) { - break; - } - try { - stream.stream.fillInput(); - } catch (EOFException e) { - break; - } catch (IOException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } + break; } int bytesWritten; try { @@ -278,8 +400,14 @@ private byte[] createByteArray(byte[] bytes, int length, int maxLength, int bufS } catch (DataFormatException e) { throw PRaiseNode.raiseStatic(nodeForRaise, ZLibError, e); } + updateGZIPCrc(stream, result, bytesWritten); baos.write(result, 0, bytesWritten); } + if (stream.gzip && stream.inflater.finished()) { + int remaining = stream.inflater.getRemaining(); + int remainingOffset = stream.inputOffset + (length - stream.inputOffset - remaining); + consumeGZIPTrailer(stream, bytes, remainingOffset, remaining, nodeForRaise); + } return baos.toByteArray(); } @@ -303,27 +431,28 @@ protected static byte[] decompress(byte[] bytes, int length, int wbits, int bufs try { if (!isReady(stream)) { isGZIPStreamReady(stream, bytes, length, true, node); - while (stream.stream.available() > 0) { - int howmany = stream.stream.read(resultArray); - baos.write(resultArray, 0, howmany); - } } else { stream.inflater.setInput(bytes, 0, length); - while (!stream.inflater.finished()) { - int howmany = stream.inflater.inflate(resultArray); - if (howmany == 0 && stream.inflater.needsInput()) { - throw PRaiseNode.raiseStatic(node, ZLibError, ErrorMessages.ERROR_5_WHILE_DECOMPRESSING); - } - baos.write(resultArray, 0, howmany); + } + while (!stream.inflater.finished()) { + int howmany = stream.inflater.inflate(resultArray); + if (howmany == 0 && stream.inflater.needsInput()) { + throw PRaiseNode.raiseStatic(node, ZLibError, ErrorMessages.ERROR_5_WHILE_DECOMPRESSING); } - stream.inflater.end(); + updateGZIPCrc(stream, resultArray, howmany); + baos.write(resultArray, 0, howmany); } - } catch (ZipException ze) { - throw PRaiseNode.raiseStatic(node, ZLibError, ze); + if (stream.gzip) { + int remaining = stream.inflater.getRemaining(); + int remainingOffset = stream.inputOffset + (length - stream.inputOffset - remaining); + consumeGZIPTrailer(stream, bytes, remainingOffset, remaining, node); + if (!stream.gzipTrailerComplete) { + throw PRaiseNode.raiseStatic(node, ZLibError, ErrorMessages.ERROR_5_WHILE_DECOMPRESSING); + } + } + stream.inflater.end(); } catch (DataFormatException e) { throw PRaiseNode.raiseStatic(node, ZLibError, ErrorMessages.WHILE_PREPARING_TO_S_DATA, "decompress"); - } catch (IOException e) { - throw new RuntimeException(e); } return baos.toByteArray(); @@ -354,6 +483,12 @@ private static boolean isReady(DecompressStream stream) { return stream.inflater != null; } + private static void updateGZIPCrc(DecompressStream stream, byte[] data, int length) { + if (stream.gzip && length > 0) { + stream.gzipCrc.update(data, 0, length); + } + } + private boolean isReady() { return isReady(stream); } @@ -370,6 +505,14 @@ private int getRemaining() { if (!isReady()) { return 0; } + if (stream.gzip) { + if (stream.gzipTrailerComplete) { + return stream.gzipUnusedLength; + } + if (stream.inflater.finished()) { + return 0; + } + } return stream.inflater.getRemaining(); } @@ -378,6 +521,6 @@ private boolean isFinished() { if (!isReady()) { return false; } - return stream.inflater.finished(); + return stream.gzip ? stream.gzipTrailerComplete : stream.inflater.finished(); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/NativeZlibCompObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/NativeZlibCompObject.java index e4190b919a..dfbcdbd308 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/NativeZlibCompObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/NativeZlibCompObject.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,36 +40,40 @@ */ package com.oracle.graal.python.builtins.modules.zlib; -import com.oracle.graal.python.runtime.NFIZlibSupport; -import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.graal.python.runtime.NativeZlibSupport; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.object.Shape; -// Note: some IDEs mark this class as inaccessible in PFactory, but changing this to -// public will cause a warning: [this-escape] possible 'this' escape before subclass is fully -// initialized public final class NativeZlibCompObject extends ZLibCompObject { - private NFIZlibSupport.Pointer pointer; + private NativeZlibSupport.Pointer pointer; byte[] lastInput; - public NativeZlibCompObject(Object cls, Shape instanceShape, Object zst, NFIZlibSupport zlibSupport) { + public NativeZlibCompObject(Object cls, Shape instanceShape, long zst, NativeZlibSupport zlibSupport) { super(cls, instanceShape); - this.pointer = new NFIZlibSupport.Pointer(this, zst, zlibSupport); - this.lastInput = null; + this.pointer = new NativeZlibSupport.Pointer(this, zst, zlibSupport); } - public Object getZst() { - assert pointer != null; - return pointer.getReference(); + public long getZst() { + NativeZlibSupport.Pointer p = pointer; + assert p != null && !p.isReleased(); + return p.getPointer(); } - @CompilerDirectives.TruffleBoundary + @TruffleBoundary public void markReleased() { - if (isInitialized) { - synchronized (this) { - isInitialized = false; - pointer.markReleased(); - pointer = null; + NativeZlibSupport.Pointer p; + synchronized (this) { + if (!isInitialized) { + assert pointer == null || pointer.isReleased(); + return; } + assert pointer != null && !pointer.isReleased(); + isInitialized = false; + p = pointer; + pointer = null; + assert !isInitialized; } + boolean markedReleased = p.markReleased(); + assert markedReleased || p.isReleased(); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZLibCompObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZLibCompObject.java index bc4f27c7b5..954ffd8868 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZLibCompObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZLibCompObject.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,7 +51,7 @@ public abstract class ZLibCompObject extends PythonBuiltinObject { private PBytes unusedData; private PBytes unconsumedTail; - public ZLibCompObject(Object cls, Shape instanceShape) { + protected ZLibCompObject(Object cls, Shape instanceShape) { super(cls, instanceShape); this.isInitialized = true; this.eof = false; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZLibModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZLibModuleBuiltins.java index 829bb32ed3..55dc6d940a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZLibModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZLibModuleBuiltins.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,7 +46,6 @@ import static com.oracle.graal.python.nodes.ErrorMessages.EXPECTED_BYTESLIKE_GOT_P; import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.ZLibError; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.crc32; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import static java.util.zip.Deflater.DEFAULT_COMPRESSION; @@ -57,9 +56,9 @@ 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.ClinicConverterFactory; import com.oracle.graal.python.annotations.ClinicConverterFactory.UseDefaultForNone; -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; @@ -84,8 +83,7 @@ import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentCastNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData; -import com.oracle.graal.python.runtime.NFIZlibSupport; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeZlibSupport; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.PythonUtils; @@ -103,8 +101,6 @@ import com.oracle.truffle.api.dsl.NonIdempotent; 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.Node; import com.oracle.truffle.api.strings.TruffleString; @@ -186,20 +182,20 @@ public void initialize(Python3Core core) { @Override public void postInitialize(Python3Core core) { super.postInitialize(core); - NFIZlibSupport zlibSupport = core.getContext().getNFIZlibSupport(); + NativeZlibSupport zlibSupport = core.getContext().getNativeZlibSupport(); PythonModule zlibModule = core.lookupBuiltinModule(T_ZLIB); // isAvailable() checked already if native access is allowed TruffleString ver = T_JDK_ZLIB_VERSION; TruffleString rtver = T_JDK_ZLIB_VERSION; if (zlibSupport.isAvailable()) { try { - ver = asString(zlibSupport.zlibVersion()); - rtver = asString(zlibSupport.zlibRuntimeVersion()); + ver = zlibSupport.zlibVersion(); + rtver = zlibSupport.zlibRuntimeVersion(); zlibModule.setAttribute(tsLiteral("Z_PARTIAL_FLUSH"), Z_PARTIAL_FLUSH); zlibModule.setAttribute(tsLiteral("Z_BLOCK"), Z_BLOCK); zlibModule.setAttribute(tsLiteral("Z_TREES"), Z_TREES); - } catch (NativeLibrary.NativeLibraryCannotBeLoaded e) { - zlibSupport.notAvailable(); + } catch (UnsupportedOperationException e) { + zlibSupport.setNotAvailable(); // ignore and proceed without native zlib support and use jdk's. } } @@ -207,17 +203,6 @@ public void postInitialize(Python3Core core) { zlibModule.setAttribute(tsLiteral("ZLIB_RUNTIME_VERSION"), rtver); } - private static TruffleString asString(Object o) { - if (o != null) { - try { - return InteropLibrary.getUncached().asTruffleString(o).switchEncodingUncached(TS_ENCODING); - } catch (UnsupportedMessageException e) { - // pass through - } - } - return null; - } - @ImportStatic(MathGuards.class) public abstract static class ExpectIntNode extends ArgumentCastNode { private final Object defaultValue; @@ -330,7 +315,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @NonIdempotent protected boolean useNative() { - return PythonContext.get(this).getNFIZlibSupport().isAvailable(); + return PythonContext.get(this).getNativeZlibSupport().isAvailable(); } @Specialization @@ -349,19 +334,17 @@ static long doCRC32(byte[] data) { @Specialization(guards = "useNative()") long doNativeBytes(PBytesLike data, int value, @Bind Node inliningTarget, - @Shared("b") @Cached SequenceStorageNodes.GetInternalBytesNode toBytes, - @Shared @Cached NativeLibrary.InvokeNativeFunction invoke) { + @Shared("b") @Cached SequenceStorageNodes.GetInternalBytesNode toBytes) { byte[] bytes = toBytes.execute(inliningTarget, data); int len = data.getSequenceStorage().length(); - return nativeCrc32(bytes, len, value, invoke); + return nativeCrc32(bytes, len, value); } @Specialization(guards = {"useNative()", "!isBytes(data)"}) long doNativeObject(VirtualFrame frame, Object data, int value, - @Shared("bb") @Cached ToBytesNode toBytesNode, - @Shared @Cached NativeLibrary.InvokeNativeFunction invoke) { + @Shared("bb") @Cached ToBytesNode toBytesNode) { byte[] bytes = toBytesNode.execute(frame, data); - return nativeCrc32(bytes, bytes.length, value, invoke); + return nativeCrc32(bytes, bytes.length, value); } @Specialization(guards = "!useNative()") @@ -386,10 +369,9 @@ static long error(Object data, @SuppressWarnings("unused") Object value, throw PRaiseNode.raiseStatic(inliningTarget, TypeError, EXPECTED_BYTESLIKE_GOT_P, data); } - long nativeCrc32(byte[] bytes, int len, int value, - NativeLibrary.InvokeNativeFunction invoke) { + long nativeCrc32(byte[] bytes, int len, int value) { PythonContext ctxt = getContext(); - int signedVal = (int) ctxt.getNFIZlibSupport().crc32(value, bytes, len, invoke); + int signedVal = (int) ctxt.getNativeZlibSupport().crc32(value, bytes, len); return signedVal & 0xFFFFFFFFL; } } @@ -410,7 +392,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @NonIdempotent protected boolean useNative() { - return getContext().getNFIZlibSupport().isAvailable(); + return getContext().getNativeZlibSupport().isAvailable(); } @Specialization @@ -429,19 +411,17 @@ private static long doAdler32(byte[] bytes) { @Specialization(guards = "useNative()") long doNativeBytes(PBytesLike data, int value, @Bind Node inliningTarget, - @Shared("b") @Cached SequenceStorageNodes.GetInternalBytesNode toBytes, - @Shared @Cached NativeLibrary.InvokeNativeFunction invoke) { + @Shared("b") @Cached SequenceStorageNodes.GetInternalBytesNode toBytes) { byte[] bytes = toBytes.execute(inliningTarget, data); int len = data.getSequenceStorage().length(); - return nativeAdler32(bytes, len, value, PythonContext.get(this), invoke); + return nativeAdler32(bytes, len, value, PythonContext.get(this)); } @Specialization(guards = {"useNative()", "!isBytes(data)"}) long doNativeObject(VirtualFrame frame, Object data, int value, - @Shared("bb") @Cached ToBytesNode toBytesNode, - @Shared @Cached NativeLibrary.InvokeNativeFunction invoke) { + @Shared("bb") @Cached ToBytesNode toBytesNode) { byte[] bytes = toBytesNode.execute(frame, data); - return nativeAdler32(bytes, bytes.length, value, PythonContext.get(this), invoke); + return nativeAdler32(bytes, bytes.length, value, PythonContext.get(this)); } @Specialization(guards = "!useNative()") @@ -461,9 +441,8 @@ long doJavaObject(VirtualFrame frame, Object data, int value, } long nativeAdler32(byte[] bytes, int len, int value, - PythonContext ctxt, - NativeLibrary.InvokeNativeFunction invoke) { - int signedVal = (int) ctxt.getNFIZlibSupport().adler32(value, bytes, len, invoke); + PythonContext ctxt) { + int signedVal = (int) ctxt.getNativeZlibSupport().adler32(value, bytes, len); return signedVal & 0xFFFFFFFFL; } @@ -523,7 +502,7 @@ abstract static class CompressInnerNode extends Node { @NonIdempotent protected boolean useNative() { - return PythonContext.get(this).getNFIZlibSupport().isAvailable(); + return PythonContext.get(this).getNativeZlibSupport().isAvailable(); } protected static boolean isValidLevel(int level) { @@ -591,7 +570,7 @@ abstract static class DecompressInnerNode extends Node { @NonIdempotent protected boolean useNative() { - return PythonContext.get(this).getNFIZlibSupport().isAvailable(); + return PythonContext.get(this).getNativeZlibSupport().isAvailable(); } @Specialization(guards = "useNative()") @@ -628,7 +607,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @NonIdempotent protected boolean useNative() { - return PythonContext.get(this).getNFIZlibSupport().isAvailable(); + return PythonContext.get(this).getNativeZlibSupport().isAvailable(); } protected static boolean isValidWBitRange(int wbits) { @@ -639,18 +618,16 @@ protected static boolean isValidWBitRange(int wbits) { static Object doNative(int level, int method, int wbits, int memLevel, int strategy, byte[] zdict, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached NativeLibrary.InvokeNativeFunction createCompObject, - @Cached NativeLibrary.InvokeNativeFunction compressObjInit, @Cached ZlibNodes.ZlibNativeErrorHandling errorHandling) { - NFIZlibSupport zlibSupport = PythonContext.get(inliningTarget).getNFIZlibSupport(); - Object zst = zlibSupport.createCompObject(createCompObject); + NativeZlibSupport zlibSupport = PythonContext.get(inliningTarget).getNativeZlibSupport(); + long zst = zlibSupport.createCompObject(); int err; if (zdict.length > 0) { err = zlibSupport.compressObjInitWithDict(zst, level, method, wbits, memLevel, strategy, - zdict, zdict.length, compressObjInit); + zdict, zdict.length); } else { - err = zlibSupport.compressObjInit(zst, level, method, wbits, memLevel, strategy, compressObjInit); + err = zlibSupport.compressObjInit(zst, level, method, wbits, memLevel, strategy); } if (err != Z_OK) { errorHandling.execute(inliningTarget, zst, err, zlibSupport, true); @@ -700,7 +677,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @NonIdempotent protected boolean useNative() { - return PythonContext.get(this).getNFIZlibSupport().isAvailable(); + return PythonContext.get(this).getNativeZlibSupport().isAvailable(); } protected static boolean isValidWBitRange(int wbits) { @@ -711,17 +688,15 @@ protected static boolean isValidWBitRange(int wbits) { static Object doNative(int wbits, byte[] zdict, @Bind Node inliningTarget, @Bind PythonContext context, - @Cached NativeLibrary.InvokeNativeFunction createCompObject, - @Cached NativeLibrary.InvokeNativeFunction decompressObjInit, @Cached ZlibNodes.ZlibNativeErrorHandling errorHandling) { - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); - Object zst = zlibSupport.createCompObject(createCompObject); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); + long zst = zlibSupport.createCompObject(); int err; if (zdict.length > 0) { - err = zlibSupport.decompressObjInitWithDict(zst, wbits, zdict, zdict.length, decompressObjInit); + err = zlibSupport.decompressObjInitWithDict(zst, wbits, zdict, zdict.length); } else { - err = zlibSupport.decompressObjInit(zst, wbits, decompressObjInit); + err = zlibSupport.decompressObjInit(zst, wbits); } if (err != Z_OK) { errorHandling.execute(inliningTarget, zst, err, zlibSupport, true); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibCompressBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibCompressBuiltins.java index 4e28ad4269..cea367d152 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibCompressBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibCompressBuiltins.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 @@ -71,8 +71,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData; -import com.oracle.graal.python.runtime.NFIZlibSupport; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeZlibSupport; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.PythonUtils; @@ -155,17 +154,14 @@ abstract static class BaseCopyNode extends PNodeWithContext { @Specialization(guards = "self.isInitialized()") static Object doNative(Node inliningTarget, NativeZlibCompObject self, @Bind PythonContext context, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction createCompObject, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction compressObjCopy, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction deallocateStream, @Cached ZlibNodes.ZlibNativeErrorHandling errorHandling) { synchronized (self) { assert self.isInitialized(); - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); - Object zstNewCopy = zlibSupport.createCompObject(createCompObject); - int err = zlibSupport.compressObjCopy(self.getZst(), zstNewCopy, compressObjCopy); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); + long zstNewCopy = zlibSupport.createCompObject(); + int err = zlibSupport.compressObjCopy(self.getZst(), zstNewCopy); if (err != Z_OK) { - zlibSupport.deallocateStream(zstNewCopy, deallocateStream); + zlibSupport.deallocateStream(zstNewCopy); errorHandling.execute(inliningTarget, self.getZst(), err, zlibSupport, false); } return PFactory.createNativeZLibCompObjectCompress(context.getLanguage(inliningTarget), zstNewCopy, zlibSupport); @@ -240,15 +236,13 @@ static PBytes empty(ZLibCompObject self, int mode, @Specialization(guards = {"mode != Z_NO_FLUSH", "self.isInitialized()"}) static PBytes doit(NativeZlibCompObject self, int mode, @Bind Node inliningTarget, - @Cached NativeLibrary.InvokeNativeFunction compressObjFlush, @Cached ZlibNodes.GetNativeBufferNode getBuffer, - @Cached NativeLibrary.InvokeNativeFunction getIsInitialised, @Cached ZlibNodes.NativeDeallocation processDeallocation, @Cached ZlibNodes.ZlibNativeErrorHandling errorHandling) { synchronized (self) { assert self.isInitialized(); PythonContext context = PythonContext.get(inliningTarget); - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); byte[] lastInput; if (self.lastInput == null) { // all previous input data has been processed or nothing has been compressed. @@ -259,12 +253,12 @@ static PBytes doit(NativeZlibCompObject self, int mode, // stored in the native stream. lastInput = self.lastInput; } - int err = zlibSupport.compressObjFlush(self.getZst(), lastInput, DEF_BUF_SIZE, mode, compressObjFlush); + int err = zlibSupport.compressObjFlush(self.getZst(), lastInput, DEF_BUF_SIZE, mode); if (err != Z_OK) { errorHandling.execute(inliningTarget, self.getZst(), err, zlibSupport, false); } byte[] resultArray = getBuffer.getOutputBuffer(inliningTarget, self.getZst(), context); - if (zlibSupport.getIsInitialised(self.getZst(), getIsInitialised) == 0) { + if (zlibSupport.getIsInitialised(self.getZst()) == 0) { processDeallocation.execute(inliningTarget, self, context, true); } return PFactory.createBytes(context.getLanguage(inliningTarget), resultArray); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressBuiltins.java index 480ce32bbe..9b8311f4df 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressBuiltins.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,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData; -import com.oracle.graal.python.runtime.NFIZlibSupport; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeZlibSupport; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; @@ -161,17 +160,14 @@ abstract static class BaseCopyNode extends PNodeWithContext { @Specialization(guards = "self.isInitialized()") static Object doNative(Node inliningTarget, NativeZlibCompObject self, @Bind PythonContext context, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction createCompObject, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction decompressObjCopy, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction deallocateStream, @Cached ZlibNodes.ZlibNativeErrorHandling errorHandling) { synchronized (self) { assert self.isInitialized(); - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); - Object zstNewCopy = zlibSupport.createCompObject(createCompObject); - int err = zlibSupport.decompressObjCopy(self.getZst(), zstNewCopy, decompressObjCopy); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); + long zstNewCopy = zlibSupport.createCompObject(); + int err = zlibSupport.decompressObjCopy(self.getZst(), zstNewCopy); if (err != Z_OK) { - zlibSupport.deallocateStream(zstNewCopy, deallocateStream); + zlibSupport.deallocateStream(zstNewCopy); errorHandling.execute(inliningTarget, self.getZst(), err, zlibSupport, false); } ZLibCompObject copy = PFactory.createNativeZLibCompObjectDecompress(context.getLanguage(inliningTarget), zstNewCopy, zlibSupport); @@ -241,20 +237,18 @@ protected ArgumentClinicProvider getArgumentClinic() { static PBytes doit(NativeZlibCompObject self, int length, @Bind Node inliningTarget, @Bind PythonContext context, - @Cached NativeLibrary.InvokeNativeFunction decompressObjFlush, @Cached ZlibNodes.GetNativeBufferNode getBuffer, - @Cached NativeLibrary.InvokeNativeFunction getIsInitialised, @Cached ZlibNodes.NativeDeallocation processDeallocation, @Cached ZlibNodes.ZlibNativeErrorHandling errorHandling) { synchronized (self) { assert self.isInitialized(); - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); - int err = zlibSupport.decompressObjFlush(self.getZst(), length, decompressObjFlush); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); + int err = zlibSupport.decompressObjFlush(self.getZst(), length); if (err != Z_OK) { errorHandling.execute(inliningTarget, self.getZst(), err, zlibSupport, false); } byte[] resultArray = getBuffer.getOutputBuffer(inliningTarget, self.getZst(), context); - if (zlibSupport.getIsInitialised(self.getZst(), getIsInitialised) == 0) { + if (zlibSupport.getIsInitialised(self.getZst()) == 0) { processDeallocation.execute(inliningTarget, self, context, false); } return PFactory.createBytes(context.getLanguage(inliningTarget), resultArray); @@ -359,12 +353,11 @@ static boolean doit(NativeZlibCompObject self) { } @Specialization(guards = {"!self.isEof()", "self.isInitialized()"}) - boolean getit(NativeZlibCompObject self, - @Cached NativeLibrary.InvokeNativeFunction getEOF) { + boolean getit(NativeZlibCompObject self) { synchronized (self) { assert self.isInitialized(); - NFIZlibSupport zlibSupport = PythonContext.get(this).getNFIZlibSupport(); - self.setEof(zlibSupport.getEOF(self.getZst(), getEOF) == 1); + NativeZlibSupport zlibSupport = PythonContext.get(this).getNativeZlibSupport(); + self.setEof(zlibSupport.getEOF(self.getZst()) == 1); return self.isEof(); } } 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..d3204ca6f7 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 @@ -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 @@ -70,8 +70,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData; -import com.oracle.graal.python.runtime.NFIZlibSupport; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeZlibSupport; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.PythonUtils; @@ -114,7 +113,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @NonIdempotent protected boolean useNative() { - return PythonContext.get(this).getNFIZlibSupport().isAvailable(); + return PythonContext.get(this).getNativeZlibSupport().isAvailable(); } protected static boolean isValidWBitRange(int wbits) { @@ -125,17 +124,15 @@ protected static boolean isValidWBitRange(int wbits) { static Object doNative(@SuppressWarnings("unused") Object type, int wbits, byte[] zdict, @Bind Node inliningTarget, @Bind PythonContext context, - @Cached NativeLibrary.InvokeNativeFunction createCompObject, - @Cached NativeLibrary.InvokeNativeFunction decompressObjInit, @Cached ZlibNodes.ZlibNativeErrorHandling errorHandling) { - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); - Object zst = zlibSupport.createCompObject(createCompObject); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); + long zst = zlibSupport.createCompObject(); int err; if (zdict.length > 0) { - err = zlibSupport.decompressObjInitWithDict(zst, wbits, zdict, zdict.length, decompressObjInit); + err = zlibSupport.decompressObjInitWithDict(zst, wbits, zdict, zdict.length); } else { - err = zlibSupport.decompressObjInit(zst, wbits, decompressObjInit); + err = zlibSupport.decompressObjInit(zst, wbits); } if (err != Z_OK) { errorHandling.execute(inliningTarget, zst, err, zlibSupport, true); @@ -204,7 +201,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 +209,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 +228,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 +239,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,23 +253,22 @@ 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()"}) - boolean doNative(ZlibDecompressorObject self, - @Cached NativeLibrary.InvokeNativeFunction getEOF) { + @Specialization(guards = {"!self.isEof()", "self.isInitialized()", "self.isNativeDecompressor()"}) + boolean doNative(ZlibDecompressorObject self) { synchronized (self) { assert self.isInitialized(); - NFIZlibSupport zlibSupport = PythonContext.get(this).getNFIZlibSupport(); - self.setEof(zlibSupport.getEOF(self.getZst(), getEOF) == 1); + NativeZlibSupport zlibSupport = PythonContext.get(this).getNativeZlibSupport(); + self.setEof(zlibSupport.getEOF(self.getZst()) == 1); return self.isEof(); } } - @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..2b54e6685e 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 @@ -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 @@ -42,7 +42,7 @@ import com.oracle.graal.python.builtins.objects.bytes.PBytes; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; -import com.oracle.graal.python.runtime.NFIZlibSupport; +import com.oracle.graal.python.runtime.NativeZlibSupport; import com.oracle.truffle.api.object.Shape; public class ZlibDecompressorObject extends PythonBuiltinObject { @@ -58,7 +58,7 @@ public class ZlibDecompressorObject extends PythonBuiltinObject { private boolean needsInput; // native - private ZlibDecompressorObject(Object cls, Shape instanceShape, Object zst, NFIZlibSupport zlibSupport) { + private ZlibDecompressorObject(Object cls, Shape instanceShape, long zst, NativeZlibSupport zlibSupport) { super(cls, instanceShape); this.compObject = new NativeZlibCompObject(cls, instanceShape, zst, zlibSupport); this.availInReal = 0; @@ -85,13 +85,13 @@ public void setEof(boolean eof) { compObject.setEof(eof); } - public Object getZst() { - assert isNative(); + public long getZst() { + assert isNativeDecompressor(); return ((NativeZlibCompObject) compObject).getZst(); } public JavaDecompress getStream() { - assert !isNative(); + assert !isNativeDecompressor(); return ((JavaDecompress) compObject); } @@ -127,11 +127,11 @@ public void setUnconsumedTail(PBytes unconsumedTail) { compObject.setUnconsumedTail(unconsumedTail); } - public boolean isNative() { + public boolean isNativeDecompressor() { return compObject instanceof NativeZlibCompObject; } - public static ZlibDecompressorObject createNative(Object cls, Shape instanceShape, Object zst, NFIZlibSupport zlibSupport) { + public static ZlibDecompressorObject createNative(Object cls, Shape instanceShape, long zst, NativeZlibSupport zlibSupport) { return new ZlibDecompressorObject(cls, instanceShape, zst, zlibSupport); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibNodes.java index dbfca15a08..549dc7a9d5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibNodes.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 @@ -58,9 +58,9 @@ import static com.oracle.graal.python.nodes.ErrorMessages.WHILE_PREPARING_TO_S_DATA; import static com.oracle.graal.python.nodes.ErrorMessages.WHILE_SETTING_ZDICT; import static com.oracle.graal.python.nodes.ErrorMessages.WHILE_S_DATA; -import static com.oracle.graal.python.runtime.NFIZlibSupport.OUTPUT_OPTION; -import static com.oracle.graal.python.runtime.NFIZlibSupport.UNCONSUMED_TAIL_OPTION; -import static com.oracle.graal.python.runtime.NFIZlibSupport.UNUSED_DATA_OPTION; +import static com.oracle.graal.python.runtime.NativeZlibSupport.OUTPUT_OPTION; +import static com.oracle.graal.python.runtime.NativeZlibSupport.UNCONSUMED_TAIL_OPTION; +import static com.oracle.graal.python.runtime.NativeZlibSupport.UNUSED_DATA_OPTION; import static com.oracle.graal.python.runtime.exception.PythonErrorType.MemoryError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.SystemError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError; @@ -71,14 +71,12 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.runtime.NFIZlibSupport; -import com.oracle.graal.python.runtime.NativeLibrary; +import com.oracle.graal.python.runtime.NativeZlibSupport; 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.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; @@ -103,9 +101,9 @@ public class ZlibNodes { public static final int Z_BUF_ERROR = -5; public static final int Z_VERSION_ERROR = -6; - protected static void deallocateStream(Object zst, NFIZlibSupport zlibSupport, NativeLibrary.InvokeNativeFunction deallocateStream, boolean deallocate) { + protected static void deallocateStream(long zst, NativeZlibSupport zlibSupport, boolean deallocate) { if (deallocate) { - zlibSupport.deallocateStream(zst, deallocateStream); + zlibSupport.deallocateStream(zst); } } @@ -117,12 +115,11 @@ public abstract static class ZlibNativeCompressObj extends PNodeWithContext { @Specialization static byte[] nativeCompress(Node inliningTarget, NativeZlibCompObject self, PythonContext context, byte[] bytes, int len, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction compressObj, @Cached GetNativeBufferNode getBuffer, @Cached ZlibNativeErrorHandling errorHandling) { - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); self.lastInput = bytes; - int err = zlibSupport.compressObj(self.getZst(), self.lastInput, len, DEF_BUF_SIZE, compressObj); + int err = zlibSupport.compressObj(self.getZst(), self.lastInput, len, DEF_BUF_SIZE); if (err != Z_OK) { errorHandling.execute(inliningTarget, self.getZst(), err, zlibSupport, false); } @@ -139,21 +136,18 @@ public abstract static class ZlibNativeCompress extends PNodeWithContext { @Specialization static byte[] nativeCompress(Node inliningTarget, byte[] bytes, int len, int level, int wbits, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction createStream, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction deallocateStream, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction deflateOffHeap, @Cached GetNativeBufferNode getBuffer, @Cached ZlibNativeErrorHandling errorHandling) { PythonContext context = PythonContext.get(inliningTarget); - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); byte[] in = bytes; - Object zst = zlibSupport.createStream(createStream); - int err = zlibSupport.deflateOffHeap(zst, in, len, DEF_BUF_SIZE, level, wbits, deflateOffHeap); + long zst = zlibSupport.createStream(); + int err = zlibSupport.deflateOffHeap(zst, in, len, DEF_BUF_SIZE, level, wbits); if (err != Z_OK) { errorHandling.execute(inliningTarget, zst, err, zlibSupport, true); } byte[] resultArray = getBuffer.getOutputBuffer(inliningTarget, zst, context); - zlibSupport.deallocateStream(zst, deallocateStream); + zlibSupport.deallocateStream(zst); return resultArray; } } @@ -166,12 +160,11 @@ public abstract static class ZlibNativeDecompressObj extends PNodeWithContext { @Specialization static byte[] nativeDecompress(Node inliningTarget, NativeZlibCompObject self, PythonContext context, byte[] bytes, int len, int maxLength, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction decompressObj, @Cached GetNativeBufferNode getBuffer, @Cached ZlibNativeErrorHandling errorHandling) { - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); byte[] in = bytes; - int err = zlibSupport.decompressObj(self.getZst(), in, len, DEF_BUF_SIZE, maxLength, decompressObj); + int err = zlibSupport.decompressObj(self.getZst(), in, len, DEF_BUF_SIZE, maxLength); if (err != Z_OK) { errorHandling.execute(inliningTarget, self.getZst(), err, zlibSupport, false); } @@ -188,12 +181,11 @@ public abstract static class ZlibNativeDecompressor extends PNodeWithContext { @Specialization static byte[] nativeDecompressBuf(Node inliningTarget, ZlibDecompressorObject self, PythonContext context, byte[] bytes, int len, int maxLength, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction decompressor, @Cached GetNativeBufferNode getBuffer, @Cached ZlibNativeErrorHandling errorHandling) { - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); byte[] in = bytes; - int ret = zlibSupport.decompressor(self.getZst(), in, len, maxLength, decompressor); + int ret = zlibSupport.decompressor(self.getZst(), in, len, maxLength); if (ret < 0) { errorHandling.execute(inliningTarget, self.getZst(), ret, zlibSupport, false); } @@ -210,20 +202,17 @@ public abstract static class ZlibNativeDecompress extends PNodeWithContext { @Specialization static byte[] nativeCompress(Node inliningTarget, byte[] bytes, int len, int wbits, int bufsize, PythonContext context, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction createStream, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction deallocateStream, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction inflateOffHeap, @Cached GetNativeBufferNode getBuffer, @Cached ZlibNativeErrorHandling errorHandling) { - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); - Object zst = zlibSupport.createStream(createStream); + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); + long zst = zlibSupport.createStream(); byte[] in = bytes; - int err = zlibSupport.inflateOffHeap(zst, in, len, bufsize, wbits, inflateOffHeap); + int err = zlibSupport.inflateOffHeap(zst, in, len, bufsize, wbits); if (err != Z_OK) { errorHandling.execute(inliningTarget, zst, err, zlibSupport, true); } byte[] resultArray = getBuffer.getOutputBuffer(inliningTarget, zst, context); - zlibSupport.deallocateStream(zst, deallocateStream); + zlibSupport.deallocateStream(zst); return resultArray; } } @@ -233,13 +222,12 @@ static byte[] nativeCompress(Node inliningTarget, byte[] bytes, int len, int wbi @GenerateCached(false) public abstract static class ZlibNativeErrorHandling extends Node { - public abstract void execute(Node inliningTarget, Object zst, int err, NFIZlibSupport zlibSupport, boolean deallocate); + public abstract void execute(Node inliningTarget, long zst, int err, NativeZlibSupport zlibSupport, boolean deallocate); @Specialization - static void doError(Object zst, int err, NFIZlibSupport zlibSupport, boolean deallocate, - @Cached(inline = false) ZlibFunctionNativeErrorHandling errorHandling, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getErrorFunction) { - errorHandling.execute(zst, zlibSupport.getErrorFunction(zst, getErrorFunction), err, zlibSupport, deallocate); + static void doError(long zst, int err, NativeZlibSupport zlibSupport, boolean deallocate, + @Cached(inline = false) ZlibFunctionNativeErrorHandling errorHandling) { + errorHandling.execute(zst, zlibSupport.getErrorFunction(zst), err, zlibSupport, deallocate); } } @@ -248,32 +236,28 @@ static void doError(Object zst, int err, NFIZlibSupport zlibSupport, boolean dea @GenerateInline(false) public abstract static class ZlibNativeErrorMsg extends Node { - public abstract void execute(Object zst, int err, TruffleString msg, NFIZlibSupport zlibSupport, boolean deallocate); + public abstract void execute(long zst, int err, TruffleString msg, NativeZlibSupport zlibSupport, boolean deallocate); @SuppressWarnings("unused") @Specialization(guards = "err == Z_VERSION_ERROR") - static void doVersionError(Object zst, int err, TruffleString msg, NFIZlibSupport zlibSupport, boolean deallocate, + static void doVersionError(long zst, int err, TruffleString msg, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("r") @Cached PRaiseNode raise) { /* * In case of a version mismatch, comp.msg won't be initialized. Check for this case * first, before looking at comp->zst.msg. */ - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, ZLibError, ERROR_D_S_S, err, msg, LIBRARY_VERSION_MISMATCH); } @Specialization(guards = "err != Z_VERSION_ERROR") - static void doError(Object zst, int err, TruffleString msg, NFIZlibSupport zlibSupport, boolean deallocate, + static void doError(long zst, int err, TruffleString msg, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, - @Shared("r") @Cached PRaiseNode raise, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction hasStreamErrorMsg, - @Exclusive @Cached NativeLibrary.InvokeNativeFunction getStreamErrorMsg) { + @Shared("r") @Cached PRaiseNode raise) { TruffleString zmsg = null; - if (zlibSupport.hasStreamErrorMsg(zst, hasStreamErrorMsg) == 1) { - zmsg = zlibSupport.getStreamErrorMsg(zst, getStreamErrorMsg); + if (zlibSupport.hasStreamErrorMsg(zst) == 1) { + zmsg = zlibSupport.getStreamErrorMsg(zst); } else { switch (err) { case Z_BUF_ERROR: @@ -287,7 +271,7 @@ static void doError(Object zst, int err, TruffleString msg, NFIZlibSupport zlibS break; } } - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); if (zmsg == null) { throw raise.raise(inliningTarget, ZLibError, ERROR_D_S, err, msg); } else { @@ -296,80 +280,76 @@ static void doError(Object zst, int err, TruffleString msg, NFIZlibSupport zlibS } } - @ImportStatic({NFIZlibSupport.class, ZLibModuleBuiltins.class}) + @ImportStatic({NativeZlibSupport.class, ZLibModuleBuiltins.class}) @GenerateUncached @GenerateInline(false) public abstract static class ZlibFunctionNativeErrorHandling extends Node { - public abstract void execute(Object zst, int function, int err, NFIZlibSupport zlibSupport, boolean deallocate); + public abstract void execute(long zst, int function, int err, NativeZlibSupport zlibSupport, boolean deallocate); @Specialization(guards = "function == DEFLATE_INIT_ERROR") - static void deflateInitError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void deflateInitError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, @Shared("r") @Cached PRaiseNode raise, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { if (err == Z_MEM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, MemoryError, OUT_OF_MEMORY_WHILE_S_DATA, "compressing"); } if (err == Z_STREAM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, ZLibError, ErrorMessages.BAD_COMPRESSION_LEVEL); } zlibError.execute(zst, err, formatNode.format(WHILE_S_DATA, "compressing"), zlibSupport, deallocate); } @Specialization(guards = "function == DEFLATE_OBJ_ERROR") - static void deflateObjInitError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void deflateObjInitError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, @Shared("r") @Cached PRaiseNode raise, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { if (err == Z_MEM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, MemoryError, CANT_ALLOCATE_MEMORY_FOR_S_OBJECT, "compression"); } if (err == Z_STREAM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, ValueError, INVALID_INITIALIZATION_OPTION); } zlibError.execute(zst, err, formatNode.format(WHILE_CREATING_S_OBJECT, "compression"), zlibSupport, deallocate); } @Specialization(guards = "function == DEFLATE_COPY_ERROR") - static void deflateCopyError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void deflateCopyError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, @Shared("r") @Cached PRaiseNode raise, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { if (err == Z_MEM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, MemoryError, CANT_ALLOCATE_MEMORY_FOR_S_OBJECT, "compression"); } if (err == Z_STREAM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, ValueError, INCONSISTENT_STREAM_STATE); } zlibError.execute(zst, err, formatNode.format(WHILE_COPYING_S_OBJECT, "compression"), zlibSupport, deallocate); } @Specialization(guards = "function == INFLATE_COPY_ERROR") - static void inflateCopyError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void inflateCopyError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, @Shared("r") @Cached PRaiseNode raise, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { if (err == Z_MEM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, MemoryError, CANT_ALLOCATE_MEMORY_FOR_S_OBJECT, "decompression"); } if (err == Z_STREAM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, ValueError, INCONSISTENT_STREAM_STATE); } zlibError.execute(zst, err, formatNode.format(WHILE_COPYING_S_OBJECT, "compression"), zlibSupport, deallocate); @@ -377,12 +357,11 @@ static void inflateCopyError(Object zst, @SuppressWarnings("unused") int functio @SuppressWarnings("unused") @Specialization(guards = "function == DEFLATE_DICT_ERROR") - static void deflateDictError(Object zst, int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void deflateDictError(long zst, int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("r") @Cached PRaiseNode raise) { if (err == Z_STREAM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, ValueError, INVALID_DICTIONARY); } throw raise.raise(inliningTarget, ValueError, ErrorMessages.DEFLATED_SET_DICT); @@ -390,14 +369,13 @@ static void deflateDictError(Object zst, int function, int err, NFIZlibSupport z } @Specialization(guards = "function == INFLATE_INIT_ERROR") - static void inflateInitError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void inflateInitError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, @Shared("r") @Cached PRaiseNode raise, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { if (err == Z_MEM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, MemoryError, OUT_OF_MEMORY_WHILE_S_DATA, "decompressing"); } zlibError.execute(zst, err, formatNode.format(WHILE_PREPARING_TO_S_DATA, "decompress"), zlibSupport, deallocate); @@ -405,82 +383,80 @@ static void inflateInitError(Object zst, @SuppressWarnings("unused") int functio } @Specialization(guards = "function == INFLATE_OBJ_ERROR") - static void inflateObjInitError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void inflateObjInitError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, @Shared("r") @Cached PRaiseNode raise, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { if (err == Z_MEM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, MemoryError, CANT_ALLOCATE_MEMORY_FOR_S_OBJECT, "decompression"); } if (err == Z_STREAM_ERROR) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, ValueError, INVALID_INITIALIZATION_OPTION); } zlibError.execute(zst, err, formatNode.format(WHILE_CREATING_S_OBJECT, "decompression"), zlibSupport, deallocate); } @Specialization(guards = "function == INFLATE_DICT_ERROR") - static void inflateDictError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void inflateDictError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Shared("err") @Cached ZlibNativeErrorMsg zlibError) { zlibError.execute(zst, err, WHILE_SETTING_ZDICT, zlibSupport, deallocate); } @Specialization(guards = "function == DEFLATE_END_ERROR") - static void deflateEndError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void deflateEndError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { zlibError.execute(zst, err, formatNode.format(WHILE_FINISHING_S, "compression"), zlibSupport, deallocate); } @Specialization(guards = "function == INFLATE_END_ERROR") - static void inflateEndError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void inflateEndError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { zlibError.execute(zst, err, formatNode.format(WHILE_FINISHING_S, "decompression"), zlibSupport, deallocate); } @Specialization(guards = "function == DEFLATE_ERROR") - static void deflateError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void deflateError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { zlibError.execute(zst, err, formatNode.format(WHILE_S_DATA, "compressing"), zlibSupport, deallocate); } @Specialization(guards = "function == INFLATE_ERROR") - static void inflateError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void inflateError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Shared("err") @Cached ZlibNativeErrorMsg zlibError, @Shared("format") @Cached SimpleTruffleStringFormatNode formatNode) { zlibError.execute(zst, err, formatNode.format(WHILE_S_DATA, "decompressing"), zlibSupport, deallocate); } @Specialization(guards = "function == DEFLATE_FLUSH_ERROR") - static void deflateFlushError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void deflateFlushError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Shared("err") @Cached ZlibNativeErrorMsg zlibError) { zlibError.execute(zst, err, WHILE_FLUSHING, zlibSupport, deallocate); } @Specialization(guards = "function == INFLATE_FLUSH_ERROR") - static void inflateFlushError(Object zst, @SuppressWarnings("unused") int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void inflateFlushError(long zst, @SuppressWarnings("unused") int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Shared("err") @Cached ZlibNativeErrorMsg zlibError) { zlibError.execute(zst, err, WHILE_FLUSHING, zlibSupport, deallocate); } @SuppressWarnings("unused") @Specialization(guards = "function == MEMORY_ERROR") - static void memError(Object zst, int function, int err, NFIZlibSupport zlibSupport, boolean deallocate, + static void memError(long zst, int function, int err, NativeZlibSupport zlibSupport, boolean deallocate, @Bind Node inliningTarget, - @Shared("d") @Cached NativeLibrary.InvokeNativeFunction deallocateStream, @Shared("r") @Cached PRaiseNode raise) { - deallocateStream(zst, zlibSupport, deallocateStream, deallocate); + deallocateStream(zst, zlibSupport, deallocate); throw raise.raise(inliningTarget, MemoryError); } @SuppressWarnings("unused") @Fallback - void fallback(Object zst, int function, int err, NFIZlibSupport zlibSupport, boolean deallocate) { + void fallback(long zst, int function, int err, NativeZlibSupport zlibSupport, boolean deallocate) { throw PRaiseNode.raiseStatic(this, SystemError, ErrorMessages.UNHANDLED_ERROR); } } @@ -494,9 +470,8 @@ public abstract static class NativeDeallocation extends PNodeWithContext { @Specialization(guards = "isCompressObj") static void doCompressObj(NativeZlibCompObject self, PythonContext context, - @SuppressWarnings("unused") boolean isCompressObj, - @Shared @Cached(inline = false) NativeLibrary.InvokeNativeFunction deallocateStream) { - context.getNFIZlibSupport().deallocateStream(self.getZst(), deallocateStream); + @SuppressWarnings("unused") boolean isCompressObj) { + context.getNativeZlibSupport().deallocateStream(self.getZst()); self.setEof(true); self.markReleased(); } @@ -505,14 +480,13 @@ static void doCompressObj(NativeZlibCompObject self, PythonContext context, static void doDecompressObj(Node inliningTarget, NativeZlibCompObject self, PythonContext context, @SuppressWarnings("unused") boolean isCompressObj, @Cached GetNativeBufferNode getUnusedDataBuffer, - @Cached GetNativeBufferNode getUnconsumedBuffer, - @Shared @Cached(inline = false) NativeLibrary.InvokeNativeFunction deallocateStream) { + @Cached GetNativeBufferNode getUnconsumedBuffer) { byte[] unusedData = getUnusedDataBuffer.getUnusedDataBuffer(inliningTarget, self.getZst(), context); PythonLanguage language = context.getLanguage(inliningTarget); self.setUnusedData(PFactory.createBytes(language, unusedData)); byte[] unconsumed = getUnconsumedBuffer.getUnconsumedTailBuffer(inliningTarget, self.getZst(), context); self.setUnconsumedTail(PFactory.createBytes(language, unconsumed)); - context.getNFIZlibSupport().deallocateStream(self.getZst(), deallocateStream); + context.getNativeZlibSupport().deallocateStream(self.getZst()); self.setEof(true); self.markReleased(); } @@ -523,31 +497,29 @@ static void doDecompressObj(Node inliningTarget, NativeZlibCompObject self, Pyth @GenerateCached(false) public abstract static class GetNativeBufferNode extends PNodeWithContext { - public abstract byte[] execute(Node inliningTarget, Object zst, int option, PythonContext context); + public abstract byte[] execute(Node inliningTarget, long zst, int option, PythonContext context); - public byte[] getOutputBuffer(Node inliningTarget, Object zst, PythonContext context) { + public byte[] getOutputBuffer(Node inliningTarget, long zst, PythonContext context) { return execute(inliningTarget, zst, OUTPUT_OPTION, context); } - public byte[] getUnusedDataBuffer(Node inliningTarget, Object zst, PythonContext context) { + public byte[] getUnusedDataBuffer(Node inliningTarget, long zst, PythonContext context) { return execute(inliningTarget, zst, UNUSED_DATA_OPTION, context); } - public byte[] getUnconsumedTailBuffer(Node inliningTarget, Object zst, PythonContext context) { + public byte[] getUnconsumedTailBuffer(Node inliningTarget, long zst, PythonContext context) { return execute(inliningTarget, zst, UNCONSUMED_TAIL_OPTION, context); } @Specialization - static byte[] getBuffer(Object zst, int option, PythonContext context, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getBufferSize, - @Cached(inline = false) NativeLibrary.InvokeNativeFunction getBuffer) { - NFIZlibSupport zlibSupport = context.getNFIZlibSupport(); - int size = zlibSupport.getBufferSize(zst, option, getBufferSize); + static byte[] getBuffer(long zst, int option, PythonContext context) { + NativeZlibSupport zlibSupport = context.getNativeZlibSupport(); + int size = zlibSupport.getBufferSize(zst, option); if (size == 0) { return PythonUtils.EMPTY_BYTE_ARRAY; } byte[] resultArray = new byte[size]; - zlibSupport.getBuffer(zst, option, resultArray, getBuffer); + zlibSupport.getBuffer(zst, option, resultArray); return resultArray; } } 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..ee391fe12e 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 @@ -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,7 +40,6 @@ */ package com.oracle.graal.python.builtins.objects; -import static com.oracle.graal.python.builtins.PythonBuiltinClassType.AttributeError; 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.PythonBuiltinClassType.ValueError; @@ -48,7 +47,6 @@ import static com.oracle.graal.python.nodes.ErrorMessages.S_MUST_BE_A_S_TUPLE; import static com.oracle.graal.python.nodes.SpecialMethodNames.T_KEYS; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___DELETE__; -import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETATTRIBUTE__; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GET__; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SET__; import static com.oracle.graal.python.nodes.StringLiterals.T_LBRACKET; @@ -69,8 +67,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; @@ -127,7 +123,6 @@ import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.call.CallNode; -import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; import com.oracle.graal.python.nodes.expression.CastToListExpressionNode.CastToListInteropNode; import com.oracle.graal.python.nodes.interop.GetInteropBehaviorNode; import com.oracle.graal.python.nodes.interop.GetInteropBehaviorValueNode; @@ -191,10 +186,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 +204,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; } @@ -624,29 +603,15 @@ public boolean hasMemberWriteSideEffects(String member, public Object invokeMember(String member, Object[] arguments, @Bind Node inliningTarget, @Exclusive @Cached TruffleString.FromJavaStringNode fromJavaStringNode, - @Exclusive @Cached LookupInheritedAttributeNode.Dynamic lookupGetattributeNode, - @Exclusive @Cached CallBinaryMethodNode callGetattributeNode, + @Exclusive @Cached PyObjectLookupAttr lookup, @Exclusive @Cached PExecuteNode executeNode, - @Exclusive @Cached InlinedConditionProfile profileGetattribute, @Exclusive @Cached InlinedConditionProfile profileMember, - // GR-44020: make shared: - @Exclusive @Cached IsBuiltinObjectProfile attributeErrorProfile, @Exclusive @Cached GilNode gil) throws UnknownIdentifierException, UnsupportedMessageException { boolean mustRelease = gil.acquire(); try { - Object memberObj; - try { - Object attrGetattribute = lookupGetattributeNode.execute(inliningTarget, this, T___GETATTRIBUTE__); - if (profileGetattribute.profile(inliningTarget, attrGetattribute == PNone.NO_VALUE)) { - throw UnknownIdentifierException.create(member); - } - memberObj = callGetattributeNode.executeObject(attrGetattribute, this, fromJavaStringNode.execute(member, TS_ENCODING)); - if (profileMember.profile(inliningTarget, memberObj == PNone.NO_VALUE)) { - throw UnknownIdentifierException.create(member); - } - } catch (PException e) { - e.expect(inliningTarget, AttributeError, attributeErrorProfile); + Object memberObj = lookup.execute(null, inliningTarget, this, fromJavaStringNode.execute(member, TS_ENCODING)); + if (profileMember.profile(inliningTarget, memberObj == PNone.NO_VALUE)) { throw UnknownIdentifierException.create(member); } return executeNode.execute(memberObj, arguments); @@ -1184,6 +1149,7 @@ static boolean access(Object object, TruffleString attrKeyName, int type, @Cached GetClassNode getClassNode, @Cached IsImmutable isImmutable, @Cached GetMroNode getMroNode, + @Cached PyObjectLookupAttr lookupAttr, @Cached GilNode gil) { boolean mustRelease = gil.acquire(); try { @@ -1203,10 +1169,14 @@ static boolean access(Object object, TruffleString attrKeyName, int type, if (attr == PNone.NO_VALUE) { attr = readObjectAttrNode.execute(owner, attrKeyName); } + Object dynamicAttr = PNone.NO_VALUE; + if (attr == PNone.NO_VALUE && (type == READABLE || type == INVOCABLE)) { + dynamicAttr = lookupAttr.execute(null, inliningTarget, object, attrKeyName); + } switch (type) { case READABLE: - return attr != PNone.NO_VALUE; + return attr != PNone.NO_VALUE || dynamicAttr != PNone.NO_VALUE; case INSERTABLE: return attr == PNone.NO_VALUE && !isImmutable.execute(inliningTarget, object); case REMOVABLE: @@ -1240,6 +1210,9 @@ static boolean access(Object object, TruffleString attrKeyName, int type, } return callableCheck.execute(inliningTarget, attr); } + if (dynamicAttr != PNone.NO_VALUE) { + return callableCheck.execute(inliningTarget, dynamicAttr); + } return false; case READ_SIDE_EFFECTS: if (attr != PNone.NO_VALUE && owner != object && !(attr instanceof PFunction || attr instanceof PBuiltinFunction)) { 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 402c0de7ab..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 @@ -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. @@ -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; @@ -43,6 +42,9 @@ import static com.oracle.graal.python.nodes.ErrorMessages.S_TAKES_AT_MOST_D_ARGUMENTS_D_GIVEN; import static com.oracle.graal.python.nodes.ErrorMessages.S_TAKES_NO_KEYWORD_ARGS; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DICT__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___CLASS_GETITEM__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___COPY__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___DEEPCOPY__; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE_EX__; import static com.oracle.graal.python.nodes.StringLiterals.T_COMMA_SPACE; import static com.oracle.graal.python.nodes.StringLiterals.T_LBRACKET; @@ -150,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; @@ -323,7 +323,7 @@ static PArray arraySequenceInitializer(VirtualFrame frame, Node inliningTarget, } } - @Specialization(guards = {"!isBytes(initializer)", "!isString(initializer)", "!isPSequence(initializer)"}) + @Specialization(guards = {"!isBytes(initializer)", "!isString(initializer)", "!isPSequence(initializer)", "!isNoValue(initializer)"}) @InliningCutoff static PArray arrayIteratorInitializer(VirtualFrame frame, Node inliningTarget, Object cls, TruffleString typeCode, Object initializer, @Cached PyObjectGetIter getIter, @@ -969,21 +969,57 @@ 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()}); } } + @Builtin(name = J___COPY__, minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class CopyNode extends PythonUnaryBuiltinNode { + @Specialization + static PArray copy(PArray self, + @CachedLibrary(limit = "2") PythonBufferAccessLibrary bufferLib, + @Bind PythonLanguage language) { + return copyArray(self, bufferLib, language); + } + } + + @Builtin(name = J___DEEPCOPY__, minNumOfPositionalArgs = 2, parameterNames = {"$self", "memo"}) + @GenerateNodeFactory + abstract static class DeepCopyNode extends PythonBinaryBuiltinNode { + @Specialization + static PArray copy(PArray self, @SuppressWarnings("unused") Object memo, + @CachedLibrary(limit = "2") PythonBufferAccessLibrary bufferLib, + @Bind PythonLanguage language) { + return copyArray(self, bufferLib, language); + } + } + + private static PArray copyArray(PArray self, PythonBufferAccessLibrary bufferLib, PythonLanguage language) { + int length = self.getLength(); + PArray newArray; + try { + newArray = PFactory.createArray(language, self.getFormatString(), self.getFormat(), length); + } catch (OverflowException e) { + // It is a copy of an existing array, the length cannot overflow. + throw CompilerDirectives.shouldNotReachHere(); + } + bufferLib.readIntoBuffer(self.getBuffer(), 0, newArray.getBuffer(), 0, self.getBytesLength(), bufferLib); + return newArray; + } + + @Builtin(name = J___CLASS_GETITEM__, minNumOfPositionalArgs = 2, isClassmethod = true) + @GenerateNodeFactory + abstract static class ClassGetItemNode extends PythonBinaryBuiltinNode { + @Specialization + static Object classGetItem(Object cls, Object key, + @Bind PythonLanguage language) { + return PFactory.createGenericAlias(language, cls, key); + } + } + @Builtin(name = J_APPEND, minNumOfPositionalArgs = 2) @GenerateNodeFactory abstract static class AppendNode extends PythonBinaryBuiltinNode { 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/asyncio/AsyncGeneratorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/AsyncGeneratorBuiltins.java index 716f7b5c8a..28c83c8b8f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/AsyncGeneratorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/AsyncGeneratorBuiltins.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 @@ -68,7 +68,6 @@ 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; @CoreFunctions(extendClasses = PythonBuiltinClassType.PAsyncGenerator) public final class AsyncGeneratorBuiltins extends PythonBuiltins { @@ -96,10 +95,8 @@ protected List> getNodeFa @GenerateNodeFactory public abstract static class GetCode extends PythonUnaryBuiltinNode { @Specialization - static Object getCode(PAsyncGen self, - @Bind Node inliningTarget, - @Cached InlinedConditionProfile hasCodeProfile) { - return self.getOrCreateCode(inliningTarget, hasCodeProfile); + static Object getCode(PAsyncGen self) { + return self.getGeneratorFunction().getCode(); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/GetAwaitableNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/GetAwaitableNode.java index 2889d5cc56..4b251dcf57 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/GetAwaitableNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/GetAwaitableNode.java @@ -62,7 +62,7 @@ import com.oracle.truffle.api.nodes.Node; @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = true) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = true) @GenerateInline(false) public abstract class GetAwaitableNode extends Node { public abstract Object execute(VirtualFrame frame, Object arg); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/ForeignBufferAcquireExports.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/ForeignBufferAcquireExports.java new file mode 100644 index 0000000000..0c31775649 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/ForeignBufferAcquireExports.java @@ -0,0 +1,299 @@ +/* + * 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.builtins.objects.buffer; + +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.BufferError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.IndexError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; + +import java.nio.ByteOrder; + +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; +import com.oracle.graal.python.util.BufferFormat; +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.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; + +@ExportLibrary(value = PythonBufferAcquireLibrary.class, receiverType = Object.class) +final class ForeignBufferAcquireExports { + + @ExportMessage + static boolean hasBuffer(Object receiver, + @CachedLibrary("receiver") InteropLibrary interop) { + return interop.hasBufferElements(receiver); + } + + @ExportMessage + static Object acquire(Object receiver, int flags, + @Bind Node inliningTarget, + @Cached CastToJavaIntExactNode castInt, + @CachedLibrary("receiver") InteropLibrary interop) { + if (!interop.hasBufferElements(receiver)) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.BYTESLIKE_OBJ_REQUIRED, receiver); + } + + long bufferSize; + try { + bufferSize = interop.getBufferSize(receiver); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + + boolean readonly; + try { + readonly = !interop.isBufferWritable(receiver); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + if (BufferFlags.requestsWritable(flags) && readonly) { + throw PRaiseNode.raiseStatic(inliningTarget, BufferError, ErrorMessages.OBJ_IS_NOT_WRITABLE); + } + return new ForeignBufferAdapter(receiver, castInt.execute(inliningTarget, bufferSize), readonly); + } + + @ExportLibrary(PythonBufferAccessLibrary.class) + static final class ForeignBufferAdapter { + final Object foreignBuffer; + final int len; + final boolean readonly; + + ForeignBufferAdapter(Object foreignBuffer, int len, boolean readonly) { + this.foreignBuffer = foreignBuffer; + this.len = len; + this.readonly = readonly; + } + + @ExportMessage + @SuppressWarnings("static-method") + boolean isBuffer() { + return true; + } + + @ExportMessage + boolean isReadonly() { + return readonly; + } + + @ExportMessage + int getBufferLength() { + return len; + } + + @ExportMessage + Object getOwner() { + return foreignBuffer; + } + + @ExportMessage + byte readByte(int byteOffset, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + try { + return interop.readBufferByte(foreignBuffer, byteOffset); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + void writeByte(int byteOffset, byte value, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + assert !readonly; + try { + interop.writeBufferByte(foreignBuffer, byteOffset, value); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + void readIntoByteArray(int srcOffset, byte[] dest, int destOffset, int length, + @Bind Node inliningTarget, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + try { + interop.readBuffer(foreignBuffer, srcOffset, dest, destOffset, length); + } catch (InvalidBufferOffsetException e) { + throw PRaiseNode.raiseStatic(inliningTarget, IndexError, ErrorMessages.STRUCT_OFFSET_OUT_OF_RANGE, e.getByteOffset(), len); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + void readIntoBuffer(int srcOffset, Object dest, int destOffset, int length, PythonBufferAccessLibrary otherLib, + @Bind Node inliningTarget, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + if (otherLib.hasInternalByteArray(dest)) { + readIntoByteArray(srcOffset, otherLib.getInternalByteArray(dest), destOffset, length, inliningTarget, interop); + } else { + for (int i = 0; i < length; i++) { + otherLib.writeByte(dest, destOffset + i, readByte(srcOffset + i, interop)); + } + } + } + + @ExportMessage + short readShortByteOrder(int byteOffset, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + try { + return interop.readBufferShort(foreignBuffer, byteOrder, byteOffset); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + int readIntByteOrder(int byteOffset, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + try { + return interop.readBufferInt(foreignBuffer, byteOrder, byteOffset); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + long readLongByteOrder(int byteOffset, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + try { + return interop.readBufferLong(foreignBuffer, byteOrder, byteOffset); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + float readFloatByteOrder(int byteOffset, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + try { + return interop.readBufferFloat(foreignBuffer, byteOrder, byteOffset); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + double readDoubleByteOrder(int byteOffset, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + try { + return interop.readBufferDouble(foreignBuffer, byteOrder, byteOffset); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + void writeShortByteOrder(int byteOffset, short value, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + assert !readonly; + try { + interop.writeBufferShort(foreignBuffer, byteOrder, byteOffset, value); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + void writeIntByteOrder(int byteOffset, int value, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + assert !readonly; + try { + interop.writeBufferInt(foreignBuffer, byteOrder, byteOffset, value); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + void writeLongByteOrder(int byteOffset, long value, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + assert !readonly; + try { + interop.writeBufferLong(foreignBuffer, byteOrder, byteOffset, value); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + void writeFloatByteOrder(int byteOffset, float value, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + assert !readonly; + try { + interop.writeBufferFloat(foreignBuffer, byteOrder, byteOffset, value); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + void writeDoubleByteOrder(int byteOffset, double value, ByteOrder byteOrder, + @CachedLibrary("this.foreignBuffer") InteropLibrary interop) { + assert !readonly; + try { + interop.writeBufferDouble(foreignBuffer, byteOrder, byteOffset, value); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + @ExportMessage + @SuppressWarnings("static-method") + int getItemSize() { + return 1; + } + + @ExportMessage + @SuppressWarnings("static-method") + TruffleString getFormatString() { + return BufferFormat.T_UINT_8_TYPE_CODE; + } + } +} 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/buffer/PythonBufferAcquireLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/PythonBufferAcquireLibrary.java index f8fa54e715..973485799e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/PythonBufferAcquireLibrary.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/PythonBufferAcquireLibrary.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 com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.library.GenerateLibrary; +import com.oracle.truffle.api.library.GenerateLibrary.DefaultExport; import com.oracle.truffle.api.library.GenerateLibrary.Abstract; import com.oracle.truffle.api.library.Library; import com.oracle.truffle.api.library.LibraryFactory; @@ -75,6 +76,7 @@ * to be released using {@link PythonBufferAccessLibrary#release(Object)} method when done. */ @GenerateLibrary(assertions = PythonBufferAcquireLibrary.Assertions.class) +@DefaultExport(ForeignBufferAcquireExports.class) public abstract class PythonBufferAcquireLibrary extends Library { /** * Return whether it is possible to acquire a read-only buffer for this object. The actual 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..ceab7c45e4 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; @@ -78,8 +81,11 @@ 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.nativeaccess.NativeMemory; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.graal.python.util.PythonUtils; @@ -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)") @@ -179,24 +187,31 @@ static PBytes doBuiltin(@SuppressWarnings("unused") Object cls, byte[] bytes, return PFactory.createBytes(language, bytes); } - @Specialization(guards = "!needsNativeAllocationNode.execute(inliningTarget, cls)") + @Specialization(guards = "!needsNativeAllocationNode.execute(inliningTarget, cls)", limit = "1") static PBytes doManaged(@SuppressWarnings("unused") Node inliningTarget, Object cls, byte[] bytes, - @SuppressWarnings("unused") @Shared @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, + @SuppressWarnings("unused") @Exclusive @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, @Cached TypeNodes.GetInstanceShape getInstanceShape) { return PFactory.createBytes(cls, getInstanceShape.execute(cls), bytes); } - @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(inline = false) CApiTransitions.PythonToNativeNode toNative, - @Cached(inline = false) CApiTransitions.NativeToPythonTransferNode toPython, - @Cached(inline = false) CExtNodes.PCallCapiFunction call) { - CArrayWrappers.CByteArrayWrapper wrapper = new CArrayWrappers.CByteArrayWrapper(bytes); + @Specialization(guards = "needsNativeAllocationNode.execute(inliningTarget, cls)", limit = "1") + static Object doNative(Node inliningTarget, Object cls, byte[] bytes, + @SuppressWarnings("unused") @Exclusive @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, + @Cached AsCharPointerNode asCharPointerNode, + @Cached CApiTransitions.PythonToNativeInternalNode toNative, + @Cached CApiTransitions.NativeToPythonInternalNode toPython) { + long dataPointer = asCharPointerNode.execute(bytes); + long clsPointer = toNative.execute(inliningTarget, 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); + long result = ExternalFunctionInvoker.invokeBYTES_SUBTYPE_NEW(null, C_API_TIMING, + context.ensureNativeContext(), BoundaryCallData.getUncached(), + context.getThreadState(context.getLanguage(inliningTarget)), callable, clsPointer, dataPointer, bytes.length); + return toPython.executeTransfer(inliningTarget, result); } 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 be1e1476e3..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 @@ -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. @@ -96,7 +96,6 @@ import com.oracle.graal.python.lib.PyNumberIndexNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; 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.SpecialAttributeNames; import com.oracle.graal.python.nodes.SpecialMethodNames; @@ -112,7 +111,6 @@ import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentCastNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; -import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; @@ -247,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, @@ -1804,30 +1808,22 @@ protected List splitDelimiter(byte[] bytes, int len, byte[] sep, int max // bytes.splitlines([keepends]) // bytearray.splitlines([keepends]) @Builtin(name = "splitlines", minNumOfPositionalArgs = 1, parameterNames = {"self", "keepends"}) + @ArgumentClinic(name = "keepends", conversion = ArgumentClinic.ClinicConversion.Boolean, defaultValue = "false") @GenerateNodeFactory - public abstract static class SplitLinesNode extends PythonBinaryBuiltinNode { + public abstract static class SplitLinesNode extends PythonBinaryClinicBuiltinNode { + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return BytesCommonBuiltinsClinicProviders.SplitLinesNodeClinicProviderGen.INSTANCE; + } + @Specialization - static PList doSplitlines(Object self, Object keependsObj, + static PList doSplitlines(Object self, boolean keepends, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached InlinedBranchProfile isPNoneProfile, - @Cached InlinedBranchProfile isBooleanProfile, - @Cached InlinedConditionProfile keependsProfile, - @Cached CastToJavaIntExactNode cast, @Cached BytesNodes.ToBytesNode toBytesNode, @Cached ListNodes.AppendNode appendNode, @Cached BytesNodes.CreateBytesNode create) { - boolean keepends; - if (keependsObj instanceof Boolean b) { - isBooleanProfile.enter(inliningTarget); - keepends = b; - } else if (PGuards.isPNone(keependsObj)) { - isPNoneProfile.enter(inliningTarget); - keepends = false; - } else { - keepends = cast.execute(inliningTarget, keependsObj) != 0; - } - keepends = keependsProfile.profile(inliningTarget, keepends); byte[] bytes = toBytesNode.execute(null, self); PList list = PFactory.createList(language); int sliceStart = 0; 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 174262daa0..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 @@ -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,10 +40,11 @@ */ package com.oracle.graal.python.builtins.objects.bytes; -import static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.createASCIIString; 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; @@ -71,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; @@ -707,8 +707,7 @@ public abstract static class ByteToHexNode extends PNodeWithContext { @Specialization(guards = "bytesPerSepGroup == 0") static TruffleString zero(byte[] argbuf, int arglen, @SuppressWarnings("unused") byte sep, @SuppressWarnings("unused") int bytesPerSepGroup, - @Shared @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { + @Shared @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { int resultlen = arglen * 2; byte[] retbuf = new byte[resultlen]; @@ -719,14 +718,13 @@ static TruffleString zero(byte[] argbuf, int arglen, @SuppressWarnings("unused") retbuf[j++] = BytesUtils.HEXDIGITS[c >>> 4]; retbuf[j++] = BytesUtils.HEXDIGITS[c & 0x0f]; } - return createASCIIString(retbuf, fromByteArrayNode, switchEncodingNode); + return fromByteArrayNode.execute(retbuf, 0, retbuf.length, TruffleString.CompactionLevel.S1, false); } @Specialization(guards = "bytesPerSepGroup < 0") static TruffleString negative(Node inliningTarget, byte[] argbuf, int arglen, byte sep, int bytesPerSepGroup, @Shared @Cached InlinedConditionProfile earlyExit, - @Shared @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Shared @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode, @Shared @Cached PRaiseNode raiseNode) { if (earlyExit.profile(inliningTarget, arglen == 0)) { return T_EMPTY_STRING; @@ -741,7 +739,7 @@ static TruffleString negative(Node inliningTarget, byte[] argbuf, int arglen, by resultlen += arglen * 2; if (absBytesPerSepGroup >= arglen) { - return zero(argbuf, arglen, sep, 0, fromByteArrayNode, switchEncodingNode); + return zero(argbuf, arglen, sep, 0, fromByteArrayNode); } byte[] retbuf = new byte[resultlen]; @@ -761,14 +759,13 @@ static TruffleString negative(Node inliningTarget, byte[] argbuf, int arglen, by retbuf[j++] = BytesUtils.HEXDIGITS[c & 0x0f]; } - return createASCIIString(retbuf, fromByteArrayNode, switchEncodingNode); + return fromByteArrayNode.execute(retbuf, 0, retbuf.length, TruffleString.CompactionLevel.S1, false); } @Specialization(guards = "absBytesPerSepGroup > 0") static TruffleString positive(Node inliningTarget, byte[] argbuf, int arglen, byte sep, int absBytesPerSepGroup, @Shared @Cached InlinedConditionProfile earlyExit, - @Shared @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Shared @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode, @Shared @Cached PRaiseNode raiseNode) { if (earlyExit.profile(inliningTarget, arglen == 0)) { return T_EMPTY_STRING; @@ -783,7 +780,7 @@ static TruffleString positive(Node inliningTarget, byte[] argbuf, int arglen, by resultlen += arglen * 2; if (absBytesPerSepGroup >= arglen) { - return zero(argbuf, arglen, sep, 0, fromByteArrayNode, switchEncodingNode); + return zero(argbuf, arglen, sep, 0, fromByteArrayNode); } byte[] retbuf = new byte[resultlen]; @@ -803,7 +800,7 @@ static TruffleString positive(Node inliningTarget, byte[] argbuf, int arglen, by retbuf[j--] = BytesUtils.HEXDIGITS[c & 0x0f]; retbuf[j--] = BytesUtils.HEXDIGITS[c >>> 4]; } - return createASCIIString(retbuf, fromByteArrayNode, switchEncodingNode); + return fromByteArrayNode.execute(retbuf, 0, retbuf.length, TruffleString.CompactionLevel.S1, false); } } @@ -1007,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/BytesUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesUtils.java index 8b6c1f2bb2..9906e17073 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesUtils.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. @@ -25,14 +25,11 @@ */ package com.oracle.graal.python.builtins.objects.bytes; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; - import java.io.ByteArrayOutputStream; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.strings.TruffleString; import com.oracle.truffle.api.strings.TruffleStringBuilder; import com.oracle.truffle.api.strings.TruffleStringBuilderUTF32; @@ -473,11 +470,6 @@ public static int digitValue(byte hexChar) { return 37; } - @TruffleBoundary - public static TruffleString createASCIIString(byte[] retbuf, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingNode) { - return switchEncodingNode.execute(fromByteArrayNode.execute(retbuf, TruffleString.Encoding.US_ASCII), TS_ENCODING); - } - @TruffleBoundary public static ByteArrayOutputStream createOutputStream() { return new ByteArrayOutputStream(); 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..7923249fed 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 @@ -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 @@ -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 { @@ -277,9 +251,9 @@ String getMetaSimpleName( @TruffleBoundary private static String getSimpleName(String fqname) { - int firstDot = fqname.indexOf('.'); - if (firstDot != -1) { - return fqname.substring(firstDot + 1); + int lastDot = fqname.lastIndexOf('.'); + if (lastDot != -1) { + return fqname.substring(lastDot + 1); } return fqname; } 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 dbc5401ef9..303358bcc1 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,19 +41,24 @@ 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.nodes.BuiltinNames.T___GRAALPYTHON__; 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; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.io.IOException; -import java.io.PrintStream; -import java.lang.invoke.VarHandle; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; @@ -73,7 +78,8 @@ 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.annotations.CApiConstant; +import com.oracle.graal.python.annotations.NativeSimpleType; 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,26 +87,26 @@ 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.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.HandlePointerConverter; +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.CExtContext; import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ApiInitException; import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ImportException; -import com.oracle.graal.python.builtins.objects.cext.common.NativePointer; 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; @@ -110,70 +116,73 @@ 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; +import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport; +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.runtime.object.PFactory; import com.oracle.graal.python.util.ConcurrentWeakSet; import com.oracle.graal.python.util.Function; 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; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.ThreadLocalAction; +import com.oracle.truffle.api.TruffleContext; import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.TruffleLanguage.Env; 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; public final class CApiContext extends CExtContext { private static final TruffleString T_PY_INIT = tsLiteral("PyInit_"); private static final TruffleString T_PY_INIT_U = tsLiteral("PyInitU_"); + private static final TruffleString T_GRAALPY_TEST_CAPI = tsLiteral("_testcapi"); + private static final TruffleString T_GRAALPY_TEST_CAPI_NAME = tsLiteral("__graalpython__._testcapi"); 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); @@ -182,13 +191,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 */ @@ -198,18 +207,23 @@ public final class CApiContext extends CExtContext { private PyCapsule pyDateTimeCAPICapsule; /** - * Same as {@link #nativeSymbolCache} if there is only one context per JVM (i.e. just one engine + * Same as {@link #nativeCAPISymbols} 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; - private static boolean nativeSymbolCacheSingleContextUsed; + @CompilationFinal(dimensions = 1) private static NativeFunctionPointer[] nativeCAPISymbolsSingleContext; + private static boolean nativeCAPISymbolsSingleContextUsed; /** - * A private (i.e. per-context) cache of C API symbols (usually helper functions). + * A private (i.e. per-context) table of native C API symbols. */ - private final Object[] nativeSymbolCache; + @CompilationFinal(dimensions = 1) private final NativeFunctionPointer[] nativeCAPISymbols; + private long nativeCAPIMetadataPtr = NULLPTR; + + 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) { } /** @@ -289,12 +303,13 @@ 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 - * objects alive. This is necessary because NFI will {@code dlclose} the library (and thus - * {@code munmap} all code) if the library object is no longer reachable. However, it can happen + * objects alive. This is necessary because native library handles may {@code dlclose} the + * library (and thus {@code munmap} all code) if the library object is no longer reachable. + * However, it can happen * that we still store raw function pointers (as Java {@code long} values) in a native object * that is referenced by a * {@link com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeObjectReference}. @@ -315,55 +330,14 @@ 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.nativeCAPISymbols = new NativeFunctionPointer[NativeCAPISymbol.values().length]; this.nativeLibraryLocator = locator; - /* - * Publish the native symbol cache to the static field if following is given: (1) The static - * field hasn't been used by another instance yet (i.e. '!used'), and (2) we are in - * single-context mode. This initialization ensures that if - * 'CApiContext.nativeSymbolCacheSingleContext != null', the context is safe to use it and - * just needs to do a null check. - */ - synchronized (CApiContext.class) { - if (!CApiContext.nativeSymbolCacheSingleContextUsed && context.getLanguage().isSingleContext()) { - assert CApiContext.nativeSymbolCacheSingleContext == null; - - assert !context.getEnv().isPreInitialization(); - - // this is the first context accessing the static symbol cache - CApiContext.nativeSymbolCacheSingleContext = this.nativeSymbolCache; - } else if (CApiContext.nativeSymbolCacheSingleContext != null) { - assert CApiContext.nativeSymbolCacheSingleContextUsed; - /* - * In this case, this context instance is at least the second one attempting to use - * the static symbol cache. We now clear the static field to indicate that every - * context instance should use its private cache. If a former context already used - * the cache and there is already compiled code, it is not necessary to invalidate - * the code because the cache is still valid. - */ - CApiContext.nativeSymbolCacheSingleContext = null; - } - 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); } @@ -385,37 +359,22 @@ public ConcurrentWeakSet getPstringInterningCache() { return pstringInterningCache; } - /** - * Tries to convert the object to a pointer (type: {@code long}) to avoid materialization of - * pointer objects. If that is not possible, the object will be returned as given. - */ - public static Object asPointer(Object ptr, InteropLibrary lib) { - if (lib.isPointer(ptr)) { - try { - return lib.asPointer(ptr); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - return ptr; - } - public long nextTssKey() { return nextTssKey.incrementAndGet(); } @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 @@ -433,12 +392,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; } /** @@ -451,40 +433,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; @@ -495,8 +473,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; } @@ -505,9 +483,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; } } @@ -519,81 +498,105 @@ public Object getModuleByIndex(int i) { } /** - * Retrieves the C API symbol cache instance in the fastest possible way. If there is just one - * instance of {@link CApiContext}, it will load the cache stored from the static field - * {@link CApiContext#nativeSymbolCacheSingleContext}. Otherwise, it will load the cache from - * the instance field {@link CApiContext#nativeSymbolCache}. + * Retrieves the C API symbol table in the fastest possible way. If there is just one instance of + * {@link CApiContext}, it will load the table stored from the static field + * {@link CApiContext#nativeCAPISymbolsSingleContext}. Otherwise, it will load the table from the + * instance field {@link CApiContext#nativeCAPISymbols}. * * @param caller The requesting node (may be {@code null}). Used for the fast-path lookup of the * {@link CApiContext} instance (if necessary). - * @return The C API symbol cache. + * @return The C API symbol table. */ - private static Object[] getSymbolCache(Node caller) { - Object[] cache = nativeSymbolCacheSingleContext; - if (cache != null) { - return cache; - } - return PythonContext.get(caller).getCApiContext().nativeSymbolCache; - } - - public static boolean isIdenticalToSymbol(Object obj, NativeCAPISymbol symbol) { - CompilerAsserts.neverPartOfCompilation(); - InteropLibrary objLib = InteropLibrary.getUncached(obj); - objLib.toNative(obj); - try { - return isIdenticalToSymbol(objLib.asPointer(obj), symbol); - } catch (UnsupportedMessageException e) { - throw new RuntimeException(e); + private static NativeFunctionPointer[] getNativeCAPISymbols(Node caller) { + NativeFunctionPointer[] symbols = nativeCAPISymbolsSingleContext; + if (symbols != null) { + return symbols; } + return PythonContext.get(caller).getCApiContext().nativeCAPISymbols; } 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 result = getNativeCAPISymbols(caller)[symbol.ordinal()]; if (result == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - result = lookupNativeSymbol(nativeSymbolCache, symbol); + CompilerDirectives.transferToInterpreter(); + throw CompilerDirectives.shouldNotReachHere("native C API symbol table entry is not initialized: " + symbol.getName()); } - assert result != null; return result; } - /** - * 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) { - 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); + private void initializeNativeCAPISymbols(NativeContext nativeContext, long nativeCAPISymbolTablePtr) { + for (NativeCAPISymbol symbol : NativeCAPISymbol.getValues()) { + long nativeSymbolPtr = NativeMemory.readPtrArrayElement(nativeCAPISymbolTablePtr, symbol.ordinal()); + assert nativeSymbolPtr != NULLPTR : symbol; + nativeCAPISymbols[symbol.ordinal()] = symbol.bind(nativeContext, nativeSymbolPtr); + } + /* + * Publish the native C API symbol table to the static field if following is given: (1) The + * static field hasn't been used by another instance yet (i.e. '!used'), and (2) we are in + * single-context mode. This initialization ensures that if + * 'CApiContext.nativeCAPISymbolsSingleContext != null', the context is safe to use it and + * just needs to do a null check. + */ + synchronized (CApiContext.class) { + if (!CApiContext.nativeCAPISymbolsSingleContextUsed && getContext().getLanguage().isSingleContext()) { + assert CApiContext.nativeCAPISymbolsSingleContext == null; + + assert !getContext().getEnv().isPreInitialization(); + + // this is the first context accessing the static C API symbol table + CApiContext.nativeCAPISymbolsSingleContext = nativeCAPISymbols; + } else if (CApiContext.nativeCAPISymbolsSingleContext != null) { + assert CApiContext.nativeCAPISymbolsSingleContextUsed; + /* + * In this case, this context instance is at least the second one attempting to use + * the static symbol table. We now clear the static field to indicate that every + * context instance should use its private table. If a former context already used + * the table and there is already compiled code, it is not necessary to invalidate + * the code because the table is still valid. + */ + CApiContext.nativeCAPISymbolsSingleContext = null; + } + CApiContext.nativeCAPISymbolsSingleContextUsed = true; + } + } + + private static void publishGraalPyTestCAPI(PythonContext context) { + if (!context.getOption(PythonOptions.EnableDebuggingBuiltins)) { + return; } + long testCAPI = context.allocateContextMemory(CStructs.GraalPy_Test_CAPI.size()); + long[] testCAPIFunctions = { + PythonCextBuiltinRegistry.GraalPyPrivate_ToNative.getNativePointer(), + PythonCextBuiltinRegistry.GraalPyPrivate_DisableReferenceQueuePolling.getNativePointer(), + PythonCextBuiltinRegistry.GraalPyPrivate_EnableReferenceQueuePolling.getNativePointer(), + PythonCextBuiltinRegistry.GraalPyPrivate_TriggerGC.getNativePointer(), + getNativeSymbol(null, NativeCAPISymbol.FUN_LONG_LV_TAG).getAddress(), + getNativeSymbol(null, NativeCAPISymbol.FUN_GRAALPY_PRIVATE_LOG_IMPL).getAddress(), + }; + NativeMemory.writePtrArrayElements(testCAPI, 0, testCAPIFunctions, 0, testCAPIFunctions.length); + + long name = context.stringToNativeUtf8Bytes(T_GRAALPY_TEST_CAPI_NAME, true); + PyCapsule capsule = PFactory.createCapsuleNativeName(context.getLanguage(), testCAPI, name); + context.lookupBuiltinModule(T___GRAALPYTHON__).setAttribute(T_GRAALPY_TEST_CAPI, capsule); + } + + public static long getNativeCAPIMetadataPointer(Node caller) { + return PythonContext.get(caller).getCApiContext().nativeCAPIMetadataPtr; } @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 } @@ -603,12 +606,11 @@ private BackgroundGCTask(PythonContext context) { super("Python GC", LOGGER); this.ctx = new WeakReference<>(context); this.rssInterval = context.getOption(PythonOptions.BackgroundGCTaskInterval); - this.gcRSSThreshold = context.getOption(PythonOptions.BackgroundGCTaskThreshold) / (double) 100; + this.gcGrowthThreshold = context.getOption(PythonOptions.BackgroundGCTaskThreshold) / (double) 100; this.gcRSSMinimum = context.getOption(PythonOptions.BackgroundGCTaskMinimum); } - Object nativeSymbol = null; - InteropLibrary callNative = null; + NativeFunctionPointer nativeSymbol = null; long currentRSS = -1; long previousRSS = -1; @@ -619,7 +621,7 @@ private BackgroundGCTask(PythonContext context) { // RSS monitor interval in ms final int rssInterval; /** - * RSS percentage increase between System.gc() calls. Low percentage will trigger + * Resources percentage increase between System.gc() calls. Low percentage will trigger * System.gc() more often which can cause unnecessary overhead. * *
    @@ -631,7 +633,7 @@ private BackgroundGCTask(PythonContext context) { * *
              */
    -        final double gcRSSThreshold;
    +        final double gcGrowthThreshold;
     
             /**
              * RSS minimum memory (in megabytes) start calling System.gc(). Default is 4GB.
    @@ -641,12 +643,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;
             }
    @@ -691,19 +692,20 @@ private void perform() {
                     return;
                 }
     
    -            if (rss < gcRSSMinimum) {
    -                return;
    -            }
    -
                 // 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;
                 }
     
    -            double ratio = ((rss - this.previousRSS) / (double) this.previousRSS);
    -            if (ratio >= gcRSSThreshold) {
    +            double ratio = ((currentWeakrefCount - this.previousWeakrefCount) / (double) Math.max(1, this.previousWeakrefCount));
    +            if (rss < gcRSSMinimum && ratio < gcGrowthThreshold) {
    +                return;
    +            }
    +
    +            ratio = ((rss - this.previousRSS) / (double) this.previousRSS);
    +            if (ratio >= gcGrowthThreshold) {
                     this.previousWeakrefCount = currentWeakrefCount;
     
                     long start = System.nanoTime();
    @@ -725,7 +727,7 @@ private void perform() {
                      * mappings (RssFile) and shmem memory (RssShmem). GC can only reduce RssAnon while
                      * RssFile is managed by the operating system which doesn't go down easily.
                      */
    -                this.previousRSS += (long) (this.previousRSS * gcRSSThreshold);
    +                this.previousRSS += (long) (this.previousRSS * gcGrowthThreshold);
                 }
             }
         }
    @@ -773,6 +775,63 @@ void runBackgroundGCTask(PythonContext context) {
     
         private Runnable nativeFinalizerRunnable;
         private Thread nativeFinalizerShutdownHook;
    +    private final ThreadLocal attachedThreadPreviousContext = new ThreadLocal<>();
    +
    +    private static final NativeSignature ATTACH_NATIVE_THREAD_SIGNATURE = NativeSignature.create(NativeSimpleType.SINT32);
    +    private static final NativeSignature DETACH_NATIVE_THREAD_SIGNATURE = NativeSignature.create(NativeSimpleType.VOID);
    +    @CApiConstant //
    +    private static final int GRAALPY_ATTACH_NATIVE_FAILED = -1;
    +    @CApiConstant //
    +    private static final int GRAALPY_ATTACH_NATIVE_OWNED = 1;
    +    @CApiConstant //
    +    private static final int GRAALPY_ATTACH_NATIVE_FOREIGN = 2;
    +    private static final MethodHandle HANDLE_ATTACH_NATIVE_THREAD;
    +    private static final MethodHandle HANDLE_DETACH_NATIVE_THREAD;
    +
    +    static {
    +        try {
    +            HANDLE_ATTACH_NATIVE_THREAD = MethodHandles.lookup().findVirtual(CApiContext.class, "attachNativeThread",
    +                            MethodType.methodType(int.class));
    +            HANDLE_DETACH_NATIVE_THREAD = MethodHandles.lookup().findVirtual(CApiContext.class, "detachNativeThread",
    +                            MethodType.methodType(void.class));
    +        } catch (NoSuchMethodException | IllegalAccessException e) {
    +            throw new RuntimeException(e);
    +        }
    +    }
    +
    +    /**
    +     * Called from native threads before using the C API. Returns
    +     * {@link #GRAALPY_ATTACH_NATIVE_OWNED} if this method entered the context and a matching
    +     * {@link #detachNativeThread()} is required, {@link #GRAALPY_ATTACH_NATIVE_FOREIGN} if the
    +     * context was already active on this thread from Truffle/Java/Python, and
    +     * {@link #GRAALPY_ATTACH_NATIVE_FAILED} if entering failed.
    +     */
    +    private int attachNativeThread() {
    +        CompilerAsserts.neverPartOfCompilation();
    +        TruffleContext truffleContext = getContext().getEnv().getContext();
    +        if (truffleContext.isActive()) {
    +            return GRAALPY_ATTACH_NATIVE_FOREIGN;
    +        }
    +        try {
    +            Object previousContext = truffleContext.enter(null);
    +            attachedThreadPreviousContext.set(previousContext);
    +            return GRAALPY_ATTACH_NATIVE_OWNED;
    +        } catch (Throwable t) {
    +            LOGGER.severe("could not attach native thread to polyglot context: " + t.getMessage());
    +            return GRAALPY_ATTACH_NATIVE_FAILED;
    +        }
    +    }
    +
    +    /**
    +     * Called from native immediately before detaching a thread that was previously attached by
    +     * {@link #attachNativeThread()}.
    +     */
    +    private void detachNativeThread() {
    +        CompilerAsserts.neverPartOfCompilation();
    +        Object previousContext = attachedThreadPreviousContext.get();
    +        attachedThreadPreviousContext.remove();
    +        getContext().getEnv().getContext().leave(null, previousContext);
    +    }
     
         @TruffleBoundary
         public static CApiContext ensureCapiWasLoaded(String reason) {
    @@ -794,7 +853,7 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
             assert PythonContext.get(null).ownsGil(); // unsafe lazy initialization
             // The initialization may run Python code (e.g., module import in
             // GraalPyPrivate_InitBuiltinTypesAndStructs), so just holding the GIL is not enough
    -        if (!context.isCApiInitialized()) {
    +        if (context.getCApiState() != PythonContext.CApiState.INITIALIZED) {
     
                 // We import those modules ahead of the initialization without the initialization lock
                 // to avoid deadlocks. We would have imported them in the initialization anyway, this
    @@ -808,17 +867,43 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
                     TruffleSafepoint.setBlockedThreadInterruptible(node, ReentrantLock::lockInterruptibly, initLock);
                 }
                 try {
    -                if (!context.isCApiInitialized()) {
    -                    // loadCApi must set C API context half-way through its execution so that it can
    -                    // run internal Java code that needs C API context
    -                    TruffleSafepoint safepoint = TruffleSafepoint.getCurrent();
    -                    boolean prevAllowSideEffects = safepoint.setAllowSideEffects(false);
    +                PythonContext.CApiState state = context.getCApiState();
    +                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."));
    +                }
    +
    +                assert state == PythonContext.CApiState.UNINITIALIZED : state;
    +                // loadCApi must set C API context half-way through its execution so that it can
    +                // run internal Java code that needs C API context
    +                TruffleSafepoint safepoint = TruffleSafepoint.getCurrent();
    +                boolean prevAllowSideEffects = safepoint.setAllowSideEffects(false);
    +                try {
    +                    CApiContext cApiContext = loadCApi(node, context, name, path, reason);
    +                    assert context.getCApiState() == PythonContext.CApiState.INITIALIZING;
    +                    initializeThreadStateCurrentForAttachedThreads(context);
    +                    CApiTransitions.initializeReferenceQueuePolling(context.handleContext);
    +                    context.runCApiHooks();
    +                    context.setCApiState(PythonContext.CApiState.INITIALIZED); // volatile write
                         try {
    -                        loadCApi(node, context, name, path, reason);
    -                        context.setCApiInitialized(); // volatile write
    -                    } finally {
    -                        safepoint.setAllowSideEffects(prevAllowSideEffects);
    +                        cApiContext.runBackgroundGCTask(context);
    +                    } catch (RuntimeException e) {
    +                        // 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;
    +                } finally {
    +                    safepoint.setAllowSideEffects(prevAllowSideEffects);
                     }
                 } finally {
                     initLock.unlock();
    @@ -827,16 +912,47 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
             return context.getCApiContext();
         }
     
    +    private static void initializeThreadStateCurrentForAttachedThreads(PythonContext context) {
    +        Thread[] threads = getOtherAliveAttachedThreads(context);
    +        if (threads.length == 0) {
    +            return;
    +        }
    +        ThreadLocalAction action = new ThreadLocalAction(true, false) {
    +            @Override
    +            protected void perform(ThreadLocalAction.Access access) {
    +                context.initializeNativeThreadState();
    +            }
    +        };
    +        context.getEnv().submitThreadLocal(threads, action);
    +    }
    +
    +    private static Thread[] getOtherAliveAttachedThreads(PythonContext context) {
    +        Thread currentThread = Thread.currentThread();
    +        ArrayList threads = new ArrayList<>();
    +        for (Thread thread : context.getThreads()) {
    +            if (thread != currentThread && thread.isAlive()) {
    +                threads.add(thread);
    +            }
    +        }
    +        return threads.toArray(Thread[]::new);
    +    }
    +
         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;
    +            if (PythonContext.isCurrentThreadVirtual()) {
    +                throw new ApiInitException(ErrorMessages.NATIVE_EXTENSIONS_VIRTUAL_THREAD);
    +            }
    +            // Check this before marking the API as loaded so that we don't get a different error
    +            // the second time a C import is attempted
    +            if (!NativeAccessSupport.isAvailable()) {
    +                throw new ImportException(null, name, path, toTruffleStringUncached(NativeContext.UNAVAILABLE));
    +            }
                 boolean useNative = true;
                 boolean isolateNative = PythonOptions.IsolateNativeModules.getValue(env.getOptions());
                 final NativeLibraryLocator loc;
    @@ -859,35 +975,52 @@ private static CApiContext loadCApi(Node node, PythonContext context, TruffleStr
                                                     "cannot use native module.", actualReason)));
                 }
                 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");
    +            long nativeCAPISymbolTablePtr = capiLibrary.lookupSymbol("GraalPy_CAPI_HELPERS");
    +            long nativeCAPIMetadataPtr = capiLibrary.lookupSymbol("GraalPy_CAPI_METADATA");
                 CApiContext cApiContext = new CApiContext(context, capiLibrary, loc);
    +            cApiContext.nativeCAPIMetadataPtr = nativeCAPIMetadataPtr;
    +            cApiContext.initializeNativeCAPISymbols(nativeContext, nativeCAPISymbolTablePtr);
                 context.setCApiContext(cApiContext);
    +            publishGraalPyTestCAPI(context);
    +            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();
    -                Object signature = env.parseInternal(Source.newBuilder(J_NFI_LANGUAGE, "(ENV,POINTER,POINTER):VOID", "exec").build()).call();
    -                initFunction = SignatureLibrary.getUncached().bind(signature, initFunction);
    -                U.execute(initFunction, builtinArrayWrapper, gcState);
    +            /*
    +             * 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 attachNativeThread = cApiContext.registerClosure("attachNativeThread", ATTACH_NATIVE_THREAD_SIGNATURE,
    +                            HANDLE_ATTACH_NATIVE_THREAD.bindTo(cApiContext), "attachNativeThread", cApiContext);
    +            long detachNativeThread = cApiContext.registerClosure("detachNativeThread", DETACH_NATIVE_THREAD_SIGNATURE,
    +                            HANDLE_DETACH_NATIVE_THREAD.bindTo(cApiContext), "detachNativeThread", cApiContext);
    +
    +            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, attachNativeThread, detachNativeThread);
    +                assert nativeThreadLocalVarPointer != NULLPTR;
    +                currentThreadState.setNativeThreadLocalVarPointer(nativeThreadLocalVarPointer);
    +            } finally {
    +                NativeMemory.free(builtinArrayPtr);
                 }
    +            CApiTransitions.initializeThreadStateDeallocatingOffsets();
     
                 assert PythonCApiAssertions.assertBuiltins(capiLibrary);
                 cApiContext.pyDateTimeCAPICapsule = PyDateTimeCAPIWrapper.initWrapper(context, cApiContext);
    -            context.runCApiHooks();
     
                 /*
                  * C++ libraries sometimes declare global objects that have destructors that call
    @@ -897,12 +1030,11 @@ 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 = getNativeSymbol(null, NativeCAPISymbol.FUN_GET_FINALIZE_CAPI_POINTER).getAddress();
    +            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);
    -                cApiContext.runBackgroundGCTask(context);
                 } catch (RuntimeException e) {
                     // This can happen when other languages restrict multithreading
                     LOGGER.warning(() -> "didn't register a native finalizer due to: " + e.getMessage());
    @@ -914,7 +1046,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()) {
    @@ -977,8 +1117,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);
    @@ -993,20 +1132,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
    @@ -1019,12 +1156,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);
         }
     
         /**
    @@ -1034,25 +1166,19 @@ public static Object loadCExtModule(Node location, PythonContext context, Module
          * finalization didn't run.
          *
          * The memory of the shared library may have been re-used if the GraalPy context was shut down
    -     * (cleanly or not), the sources were collected, and NFI's mechanism for unloading libraries
    -     * triggered a dlclose that dropped the refcount of the python-native library to 0. We leak 1
    +     * (cleanly or not), the sources were collected, and native library unloading 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);
             }
         }
     
    @@ -1076,21 +1202,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 {
    @@ -1106,18 +1226,27 @@ public void finalizeCApi() {
              * allocated resources (e.g. native object stubs). Calling
              * 'CApiTransitions.pollReferenceQueue' could then lead to a double-free.
              */
    -        CApiTransitions.disableReferenceQueuePolling(handleContext);
    +        CApiTransitions.disableReferenceQueuePollingPermanently(handleContext);
     
             TruffleSafepoint sp = TruffleSafepoint.getCurrent();
             boolean prev = sp.setAllowActions(false);
             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);
    +                // Thread-state singleton fields point to handle-table stubs. Clear those roots
    +                // before the generic native stub cleanup below frees the stubs.
    +                context.clearNativeThreadStateSingletons();
                     // Now we can clear all native memory that was simply allocated from Java. This
                     // must be done after the the singleton wrappers were cleared because they might
                     // also end up in the lookup table and may otherwise be double-freed.
    @@ -1129,7 +1258,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 {
    @@ -1146,14 +1275,14 @@ public void finalizeCApi() {
             pyCFunctionWrappers.clear();
             freeGCState();
             /*
    -         * If the static symbol cache is not null, then it is guaranteed that this context instance
    +         * If the static symbol table is not null, then it is guaranteed that this context instance
              * was the exclusive user of it. We can now reset the state such that other contexts created
              * after this can use it.
              */
             synchronized (CApiContext.class) {
    -            if (nativeSymbolCacheSingleContext != null) {
    -                nativeSymbolCacheSingleContext = null;
    -                nativeSymbolCacheSingleContextUsed = false;
    +            if (nativeCAPISymbolsSingleContext != null) {
    +                nativeCAPISymbolsSingleContext = null;
    +                nativeCAPISymbolsSingleContextUsed = false;
                 }
             }
     
    @@ -1163,33 +1292,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, NativeToPythonInternalNode.executeUncached(nativeResult, false));
             if (!(result instanceof PythonModule)) {
                 // Multi-phase extension module initialization
     
    @@ -1205,7 +1319,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;
    @@ -1217,8 +1331,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);
                 }
    @@ -1235,114 +1349,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);
    @@ -1355,36 +1367,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);
         }
     
         /**
    @@ -1399,8 +1400,4 @@ public Object getOrAllocateNativePyMethodDef(PyMethodDefHelper pyMethodDef) {
         public PyCFunctionWrapper getOrCreatePyCFunctionWrapper(RootCallTarget ct, Function cons) {
             return pyCFunctionWrappers.computeIfAbsent(ct, cons);
         }
    -
    -    public static boolean isPointerObject(Object object) {
    -        return object.getClass() == NativePointer.class || object.getClass().getSimpleName().contains("NFIPointer") || object.getClass().getSimpleName().contains("LLVMPointer");
    -    }
     }
    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..c05ec2e0c4 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
    @@ -130,6 +130,7 @@
     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.PyObjectPtr;
     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.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.PySendResult;
     import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PySliceObject;
    @@ -234,6 +235,7 @@ public final class CApiFunction {
         @CApiBuiltin(name = "PyCFunction_New", ret = PyObject, args = {PyMethodDef, PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyCFunction_NewEx", ret = PyObject, args = {PyMethodDef, PyObject, PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyCMethod_New", ret = PyObject, args = {PyMethodDef, PyObject, PyObject, PyTypeObject}, call = CImpl)
    +    @CApiBuiltin(name = "PyCallable_Check", ret = PrimitiveResult32, args = {PyObjectReturn}, call = CImpl)
         @CApiBuiltin(name = "PyUnstable_Code_New", ret = PyCodeObject, args = {Int, Int, Int, Int, Int, PyObject, PyObject, PyObject, PyObject, PyObject, PyObject, PyObject, PyObject, PyObject, Int,
                         PyObject, PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyCodec_StrictErrors", ret = PyObject, args = {PyObject}, call = CImpl)
    @@ -248,6 +250,7 @@ public final class CApiFunction {
         @CApiBuiltin(name = "PyDict_DelItemString", ret = Int, args = {PyObject, ConstCharPtrAsTruffleString}, call = CImpl)
         @CApiBuiltin(name = "PyDict_GetItemString", ret = PyObject, args = {PyObject, ConstCharPtrAsTruffleString}, call = CImpl)
         @CApiBuiltin(name = "PyDict_Next", ret = Int, args = {PyObject, PY_SSIZE_T_PTR, PyObjectPtr, PyObjectPtr}, call = CImpl)
    +    @CApiBuiltin(name = "PyDict_Size", ret = Py_ssize_t, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyDict_SetItemString", ret = Int, args = {PyObject, ConstCharPtrAsTruffleString, PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyErr_BadArgument", ret = Int, args = {}, call = CImpl)
         @CApiBuiltin(name = "PyErr_BadInternalCall", ret = Void, args = {}, call = CImpl)
    @@ -319,6 +322,7 @@ public final class CApiFunction {
         @CApiBuiltin(name = "PyInterpreterState_GetDict", ret = PyObject, args = {PyInterpreterState}, call = CImpl)
         @CApiBuiltin(name = "PyInterpreterState_GetID", ret = INT64_T, args = {PyInterpreterState}, call = CImpl)
         @CApiBuiltin(name = "PyInterpreterState_Main", ret = PyInterpreterState, args = {}, call = CImpl)
    +    @CApiBuiltin(name = "PyIter_Check", ret = Int, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyIter_Send", ret = PySendResult, args = {PyObject, PyObject, PyObjectPtr}, call = CImpl)
         @CApiBuiltin(name = "PyList_SetItem", ret = Int, args = {PyObject, Py_ssize_t, PyObjectTransfer}, call = CImpl)
         @CApiBuiltin(name = "PyLong_AsDouble", ret = Double, args = {PyObject}, call = CImpl)
    @@ -328,14 +332,22 @@ public final class CApiFunction {
         @CApiBuiltin(name = "PyLong_AsLongLongAndOverflow", ret = LONG_LONG, args = {PyObject, INT_LIST}, call = CImpl)
         @CApiBuiltin(name = "PyLong_AsSize_t", ret = SIZE_T, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyLong_AsSsize_t", ret = Py_ssize_t, args = {PyObject}, call = CImpl)
    +    @CApiBuiltin(name = "PyLong_AsVoidPtr", ret = Pointer, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyLong_AsUnsignedLong", ret = UNSIGNED_LONG, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyLong_AsUnsignedLongLong", ret = UNSIGNED_LONG_LONG, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyLong_AsUnsignedLongLongMask", ret = UNSIGNED_LONG_LONG, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyLong_AsUnsignedLongMask", ret = UNSIGNED_LONG, args = {PyObject}, call = CImpl)
    +    @CApiBuiltin(name = "PyUnstable_Long_IsCompact", ret = Int, args = {ConstPyLongObject}, call = CImpl)
    +    @CApiBuiltin(name = "_PyLong_NumBits", ret = SIZE_T, args = {PyObject}, call = CImpl)
    +    @CApiBuiltin(name = "_PyLong_Sign", ret = Int, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyLong_FromString", ret = PyObject, args = {ConstCharPtrAsTruffleString, CHAR_PTR_LIST, Int}, call = CImpl)
    +    @CApiBuiltin(name = "PyLong_FromDouble", ret = PyObjectTransfer, args = {ArgDescriptor.Double}, call = CImpl)
         @CApiBuiltin(name = "PyLong_FromLong", ret = PyObjectTransfer, args = {ArgDescriptor.Long}, call = CImpl)
         @CApiBuiltin(name = "PyLong_FromLongLong", ret = PyObjectTransfer, args = {LONG_LONG}, call = CImpl)
    +    @CApiBuiltin(name = "PyLong_FromSize_t", ret = PyObjectTransfer, args = {SIZE_T}, call = CImpl)
         @CApiBuiltin(name = "PyLong_FromSsize_t", ret = PyObjectTransfer, args = {Py_ssize_t}, call = CImpl)
    +    @CApiBuiltin(name = "PyLong_FromUnsignedLong", ret = PyObjectTransfer, args = {UNSIGNED_LONG}, call = CImpl)
    +    @CApiBuiltin(name = "PyLong_FromUnsignedLongLong", ret = PyObjectTransfer, args = {UNSIGNED_LONG_LONG}, call = CImpl)
         @CApiBuiltin(name = "PyLong_FromVoidPtr", ret = PyObject, args = {Pointer}, call = CImpl)
         @CApiBuiltin(name = "PyMapping_Check", ret = Int, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyMapping_GetItemString", ret = PyObject, args = {PyObject, ConstCharPtrAsTruffleString}, call = CImpl)
    @@ -375,6 +387,10 @@ 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_Index", ret = PyObject, args = {PyObject}, call = CImpl)
    +    @CApiBuiltin(name = "PyNumber_Index", 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)
    @@ -438,9 +454,11 @@ public final class CApiFunction {
         @CApiBuiltin(name = "PyObject_GetAttr", ret = PyObject, args = {PyObject, PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyObject_GetAttrString", ret = PyObject, args = {PyObject, ConstCharPtrAsTruffleString}, call = CImpl)
         @CApiBuiltin(name = "PyObject_GetBuffer", ret = Int, args = {PyObject, PY_BUFFER_PTR, Int}, call = CImpl)
    +    @CApiBuiltin(name = "PyObject_GetIter", ret = PyObject, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyObject_Init", ret = PyObject, args = {PyObject, PyTypeObject}, call = CImpl)
         @CApiBuiltin(name = "PyObject_InitVar", ret = PyVarObject, args = {PyVarObject, PyTypeObject, Py_ssize_t}, call = CImpl)
         @CApiBuiltin(name = "PyObject_IS_GC", ret = Int, args = {PyObject}, call = CImpl)
    +    @CApiBuiltin(name = "PyObject_IsTrue", ret = Int, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyObject_Malloc", ret = Pointer, args = {SIZE_T}, call = CImpl)
         @CApiBuiltin(name = "PyObject_Not", ret = Int, args = {PyObject}, call = CImpl)
         @CApiBuiltin(name = "PyObject_Print", ret = Int, args = {PyObject, FILE_PTR, Int}, call = CImpl)
    @@ -492,6 +510,7 @@ public final class CApiFunction {
         @CApiBuiltin(name = "PyThread_tss_set", ret = Int, args = {PY_TSS_T_PTR, Pointer}, call = CImpl)
         @CApiBuiltin(name = "PyTraceMalloc_Track", ret = Int, args = {UNSIGNED_INT, UINTPTR_T, SIZE_T}, call = CImpl)
         @CApiBuiltin(name = "PyTraceMalloc_Untrack", ret = Int, args = {UNSIGNED_INT, UINTPTR_T}, call = CImpl)
    +    @CApiBuiltin(name = "PyTuple_New", ret = PyObject, args = {Py_ssize_t}, call = CImpl)
         @CApiBuiltin(name = "PyTuple_Pack", ret = PyObject, args = {Py_ssize_t, VARARGS}, call = CImpl)
         @CApiBuiltin(name = "PyTuple_GetItem", ret = PyObjectBorrowed, args = {PyObject, Py_ssize_t}, call = CImpl)
         @CApiBuiltin(name = "PyTuple_SetItem", ret = Int, args = {PyObject, Py_ssize_t, PyObjectTransfer}, call = CImpl)
    @@ -837,10 +856,10 @@ public final class CApiFunction {
         @CApiBuiltin(name = "PyODict_DelItem", ret = Int, args = {PyObject, PyObject}, call = NotImplemented)
         @CApiBuiltin(name = "PyODict_New", ret = PyObject, args = {}, call = NotImplemented)
         @CApiBuiltin(name = "PyODict_SetItem", ret = Int, args = {PyObject, PyObject, PyObject}, call = NotImplemented)
    -    @CApiBuiltin(name = "PyOS_AfterFork", ret = Void, args = {}, call = NotImplemented)
    -    @CApiBuiltin(name = "PyOS_AfterFork_Child", ret = Void, args = {}, call = NotImplemented)
    -    @CApiBuiltin(name = "PyOS_AfterFork_Parent", ret = Void, args = {}, call = NotImplemented)
    -    @CApiBuiltin(name = "PyOS_BeforeFork", ret = Void, args = {}, call = NotImplemented)
    +    @CApiBuiltin(name = "PyOS_AfterFork", ret = Void, args = {}, call = CImpl)
    +    @CApiBuiltin(name = "PyOS_AfterFork_Child", ret = Void, args = {}, call = CImpl)
    +    @CApiBuiltin(name = "PyOS_AfterFork_Parent", ret = Void, args = {}, call = CImpl)
    +    @CApiBuiltin(name = "PyOS_BeforeFork", ret = Void, args = {}, call = CImpl)
         @CApiBuiltin(name = "PyOS_InterruptOccurred", ret = Int, args = {}, call = NotImplemented)
         @CApiBuiltin(name = "PyOS_Readline", ret = CHAR_PTR, args = {FILE_PTR, FILE_PTR, ConstCharPtrAsTruffleString}, call = NotImplemented)
         @CApiBuiltin(name = "PyOS_getsig", ret = PY_OS_SIGHANDLER, args = {Int}, call = NotImplemented)
    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..2ffd7de03f 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
    @@ -40,13 +40,17 @@
      */
     package com.oracle.graal.python.builtins.objects.cext.capi;
     
    +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
    +
     import com.oracle.graal.python.PythonLanguage;
     import com.oracle.graal.python.annotations.Builtin;
     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.NativeCharToTruffleStringNodeGen;
     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.StringAsPythonStringNodeGen;
     import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.WriteByteNodeGen;
     import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.WriteCharNodeGen;
     import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.WriteDoubleNodeGen;
    @@ -59,24 +63,21 @@
     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.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.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.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;
    -import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.NativeUnsignedByteNodeGen;
    -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.ints.PInt;
     import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode;
    +import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
     import com.oracle.graal.python.nodes.ErrorMessages;
     import com.oracle.graal.python.nodes.PRaiseNode;
     import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode;
    @@ -85,18 +86,23 @@
     import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
     import com.oracle.graal.python.nodes.object.GetClassNode;
     import com.oracle.graal.python.runtime.exception.PException;
    +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
     import com.oracle.graal.python.runtime.object.PFactory;
     import com.oracle.graal.python.util.PythonUtils.PrototypeNodeFactory;
    +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.dsl.Bind;
     import com.oracle.truffle.api.dsl.Cached;
     import com.oracle.truffle.api.dsl.GenerateInline;
    +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.nodes.Node;
     import com.oracle.truffle.api.strings.TruffleString;
    +import com.oracle.truffle.api.strings.TruffleString.Encoding;
    +import com.oracle.truffle.api.strings.TruffleString.FromNativePointerNode;
    +import com.oracle.truffle.api.strings.TruffleString.SwitchEncodingNode;
     
     public class CApiMemberAccessNodes {
     
    @@ -130,144 +136,170 @@ 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;
    +    /** Implements case {@code T_STRING} of function {@code PyMember_GetOne}. Just wraps {@link FromCharPointerNode} to fulfill the interface. */
    +    @GenerateInline(false)
    +    public abstract static class StringAsPythonStringNode extends CExtToJavaNode {
    +
    +        @Specialization
    +        static TruffleString doNative(long value,
    +                        @Bind Node inliningTarget,
    +                        @Cached FromCharPointerNode fromPtr) {
    +            assert value != NativeMemory.NULLPTR;
    +            return fromPtr.execute(inliningTarget, value);
             }
    -        throw CompilerDirectives.shouldNotReachHere("invalid member type");
         }
     
    -    private static CExtAsPythonObjectNode getReadConverterNode(int type) {
    -        switch (type) {
    -            case T_SHORT:
    -            case T_INT:
    -            case T_LONG:
    -            case T_LONGLONG:
    -            case T_FLOAT:
    -            case T_DOUBLE:
    -            case T_BYTE:
    -            case T_PYSSIZET:
    -            case T_NONE:
    -                // no conversion needed
    -                return null;
    -            case T_STRING:
    -            case T_STRING_INPLACE:
    -                return StringAsPythonStringNodeGen.create();
    -            case T_BOOL:
    -                return NativePrimitiveAsPythonBooleanNodeGen.create();
    -            case T_CHAR:
    -                return NativePrimitiveAsPythonCharNodeGen.create();
    -            case T_UBYTE:
    -                return NativeUnsignedByteNodeGen.create();
    -            case T_USHORT:
    -                return NativeUnsignedShortNodeGen.create();
    -            case T_UINT:
    -            case T_ULONG:
    -            case T_ULONGLONG:
    -                return NativeUnsignedPrimitiveAsPythonObjectNodeGen.create();
    -            case T_OBJECT:
    -            case T_OBJECT_EX:
    -                return null;
    +    /**
    +     * Implements case {@code T_CHAR} of function {@code PyMember_GetOne}. Expects a pointer that points to a single native {@code char} which represents an UTF-8 code point.
    +     */
    +    @GenerateInline(false)
    +    abstract static class NativeCharToTruffleStringNode extends CExtToJavaNode {
    +        @Specialization
    +        static TruffleString doLong(long ptr,
    +                        @Cached FromNativePointerNode fromNativePointerNode,
    +                        @Cached SwitchEncodingNode switchEncodingNode) {
    +            TruffleString ts = fromNativePointerNode.execute(ptr, 0, 1, Encoding.UTF_8, true);
    +            return switchEncodingNode.execute(ts, TS_ENCODING);
    +        }
    +    }
    +
    +    static final class NoConversionRequiredNode extends CExtToJavaNode {
    +
    +        static final NoConversionRequiredNode INSTANCE = new NoConversionRequiredNode();
    +
    +        private NoConversionRequiredNode() {
    +        }
    +
    +        @Override
    +        public Object execute(@SuppressWarnings("unused") long pointer) {
    +            throw CompilerDirectives.shouldNotReachHere();
    +        }
    +
    +        @Override
    +        public boolean isAdoptable() {
    +            return false;
             }
    -        throw CompilerDirectives.shouldNotReachHere("invalid member type");
         }
     
         @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;
    -
             /** The specified member type. */
             private final int type;
     
             /** 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) {
                 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 PythonToNativeInternalNode toNativeNode,
    +                        @Cached("createAsPythonObjectNode()") CExtToJavaNode asPythonObjectNode,
                             @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);
    +            CompilerAsserts.partialEvaluationConstant(type);
    +            CompilerAsserts.partialEvaluationConstant(offset);
    +
    +            // in case of T_LONGLONG or T_ULONGLONG, we assume that 'sizeof(long long)' is 'Long.BYTES'
    +            assert type != T_LONGLONG && type != T_ULONGLONG || CStructs.long__long.size() == Long.BYTES;
    +
    +            // in case of T_PYSSIZET, we assume that 'sizeof(Py_ssize_t)' is 'Long.BYTES'
    +            assert type != T_PYSSIZET || CStructs.Py_ssize_t.size() == Long.BYTES;
    +
    +            long selfPtr = toNativeNode.execute(inliningTarget, self);
    +            long memberPtr = NativeMemory.getFieldPtr(selfPtr, offset);
    +            long addr;
    +            switch (type) {
    +                case T_BOOL -> {
    +                    return NativeMemory.readByte(memberPtr) != 0;
    +                }
    +                case T_CHAR, T_STRING_INPLACE -> addr = memberPtr;
    +                case T_BYTE -> {
    +                    byte b = NativeMemory.readByte(memberPtr);
    +                    if (isCharSigned()) {
    +                        return (int) b;
    +                    }
    +                    return Byte.toUnsignedInt(b);
    +                }
    +                case T_UBYTE -> {
    +                    return Byte.toUnsignedInt(NativeMemory.readByte(memberPtr));
    +                }
    +                case T_SHORT -> {
    +                    return (int) NativeMemory.readShort(memberPtr);
    +                }
    +                case T_USHORT -> {
    +                    return ((int) NativeMemory.readShort(memberPtr)) & 0xffff;
    +                }
    +                case T_INT -> {
    +                    return NativeMemory.readInt(memberPtr);
    +                }
    +                case T_UINT -> {
    +                    return NativeMemory.readInt(memberPtr) & 0xffffffffL;
    +                }
    +                case T_LONG, T_LONGLONG, T_PYSSIZET -> {
    +                    return NativeMemory.readLong(memberPtr);
                     }
    -                if (type == T_OBJECT && nativeResult == PNone.NO_VALUE) {
    -                    nativeResult = PNone.NONE;
    +                case T_ULONG, T_ULONGLONG -> {
    +                    long l = NativeMemory.readLong(memberPtr);
    +                    if (l >= 0) {
    +                        return l;
    +                    }
    +                    return PFactory.createInt(PythonLanguage.get(this), PInt.longToUnsignedBigInteger(l));
                     }
    -                if (asPythonObjectNode != null) {
    -                    return asPythonObjectNode.execute(nativeResult);
    -                } else {
    -                    return nativeResult;
    +                case T_FLOAT -> {
    +                    return (double) NativeMemory.readFloat(memberPtr);
                     }
    +                case T_DOUBLE -> {
    +                    return NativeMemory.readDouble(memberPtr);
    +                }
    +                case T_OBJECT, T_STRING -> {
    +                    addr = NativeMemory.readPtr(memberPtr);
    +                    if (addr == NativeMemory.NULLPTR) {
    +                        return PNone.NONE;
    +                    }
    +                }
    +                case T_OBJECT_EX -> {
    +                    addr = NativeMemory.readPtr(memberPtr);
    +                    if (addr == NativeMemory.NULLPTR) {
    +                        throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError);
    +                    }
    +                }
    +                case T_NONE -> {
    +                    return PNone.NONE;
    +                }
    +                default -> throw CompilerDirectives.shouldNotReachHere("invalid member type");
                 }
    +
    +            assert addr != NativeMemory.NULLPTR;
    +            return asPythonObjectNode.execute(addr);
             }
     
    -        private PythonToNativeNode ensureToSulongNode() {
    -            if (toSulongNode == null) {
    -                CompilerDirectives.transferToInterpreterAndInvalidate();
    -                toSulongNode = insert(PythonToNativeNodeGen.create());
    -            }
    -            return toSulongNode;
    +        @NeverDefault
    +        final CExtToJavaNode createAsPythonObjectNode() {
    +            return switch (type) {
    +                case T_CHAR -> NativeCharToTruffleStringNodeGen.create();
    +                case T_STRING, T_STRING_INPLACE -> StringAsPythonStringNodeGen.create();
    +                case T_OBJECT, T_OBJECT_EX -> NativeToPythonNode.create();
    +                default -> NoConversionRequiredNode.INSTANCE;
    +            };
             }
     
             @TruffleBoundary
             public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, Object owner, TruffleString propertyName, int type, int offset) {
    -            CExtAsPythonObjectNode asPythonObjectNode = getReadConverterNode(type);
    -            RootCallTarget callTarget = language.createCachedPropAccessCallTarget(
    -                            l -> new BuiltinFunctionRootNode(l, BUILTIN, new PrototypeNodeFactory<>(ReadMemberNodeGen.create(type, offset, asPythonObjectNode)), true),
    +            BuiltinFunctionRootNode rootNode = language.createCachedPropAccessRootNode(
    +                            l -> new BuiltinFunctionRootNode(l, BUILTIN, new PrototypeNodeFactory<>(ReadMemberNodeGen.create(type, offset)), true),
                                 ReadMemberNode.class, BUILTIN.name(), type, offset);
    -            int flags = PBuiltinFunction.getFlags(BUILTIN, callTarget);
    -            return PFactory.createBuiltinFunction(language, propertyName, owner, 0, flags, callTarget);
    +            int flags = PBuiltinFunction.getFlags(BUILTIN, rootNode.getSignature());
    +            return PFactory.createBuiltinFunction(language, propertyName, owner, 0, flags, rootNode);
             }
         }
     
    @@ -289,11 +321,11 @@ Object doGeneric(Object self, Object value,
     
             @TruffleBoundary
             public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString propertyName) {
    -            RootCallTarget builtinCt = language.createCachedCallTarget(
    +            BuiltinFunctionRootNode rootNode = language.createCachedRootNode(
                                 l -> new BuiltinFunctionRootNode(l, BUILTIN, new PrototypeNodeFactory<>(ReadOnlyMemberNodeGen.create(propertyName)), true),
                                 ReadOnlyMemberNode.class, BUILTIN.name());
    -            int flags = PBuiltinFunction.getFlags(BUILTIN, builtinCt);
    -            return PFactory.createBuiltinFunction(language, propertyName, null, 0, flags, builtinCt);
    +            int flags = PBuiltinFunction.getFlags(BUILTIN, rootNode.getSignature());
    +            return PFactory.createBuiltinFunction(language, propertyName, null, 0, flags, rootNode);
             }
         }
     
    @@ -313,27 +345,26 @@ static Object doGeneric(Object self, @SuppressWarnings("unused") Object value,
     
             @TruffleBoundary
             public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString propertyName) {
    -            RootCallTarget builtinCt = language.createCachedCallTarget(
    +            BuiltinFunctionRootNode rootNode = language.createCachedRootNode(
                                 l -> new BuiltinFunctionRootNode(l, BUILTIN, new PrototypeNodeFactory<>(BadMemberDescrNodeGen.create()), true),
                                 BadMemberDescrNode.class, BUILTIN.name());
    -            int flags = PBuiltinFunction.getFlags(BUILTIN, builtinCt);
    -            return PFactory.createBuiltinFunction(language, propertyName, null, 0, flags, builtinCt);
    +            int flags = PBuiltinFunction.getFlags(BUILTIN, rootNode.getSignature());
    +            return PFactory.createBuiltinFunction(language, propertyName, null, 0, flags, rootNode);
             }
         }
     
         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 +372,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 +382,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 +392,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 +405,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 +415,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 +440,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 +453,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 +463,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 +476,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 +486,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 +497,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 +507,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 +524,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 +576,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 +591,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.execute(self) + offset;
     
                 /*
                  * Deleting values is only allowed for members with object type (see structmember.c:
    @@ -623,11 +644,11 @@ public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, Ob
                     return BadMemberDescrNode.createBuiltinFunction(language, propertyName);
                 }
                 //
    -            RootCallTarget callTarget = language.createCachedPropAccessCallTarget(
    +            BuiltinFunctionRootNode rootNode = language.createCachedPropAccessRootNode(
                                 l -> new BuiltinFunctionRootNode(l, BUILTIN, new PrototypeNodeFactory<>(WriteMemberNodeGen.create(type, offset)), true),
                                 WriteMemberNode.class, BUILTIN.name(), type, offset);
    -            int flags = PBuiltinFunction.getFlags(BUILTIN, callTarget);
    -            return PFactory.createBuiltinFunction(language, propertyName, owner, 0, flags, callTarget);
    +            int flags = PBuiltinFunction.getFlags(BUILTIN, rootNode.getSignature());
    +            return PFactory.createBuiltinFunction(language, propertyName, owner, 0, flags, rootNode);
             }
         }
     }
    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 3aed43b991..8c390f2bbe 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,23 +61,30 @@
     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.modules.cext.CFunctionDocUtils;
     import com.oracle.graal.python.builtins.objects.PNone;
     import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
     import com.oracle.graal.python.builtins.objects.bytes.PByteArray;
    @@ -88,42 +92,27 @@
     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.NativeToPythonTransferNode;
    -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.NativeToPythonClassInternalNode;
    +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.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;
     import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
     import com.oracle.graal.python.builtins.objects.complex.PComplex;
    -import com.oracle.graal.python.builtins.objects.dict.PDict;
     import com.oracle.graal.python.builtins.objects.floats.PFloat;
     import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
     import com.oracle.graal.python.builtins.objects.function.PKeyword;
    @@ -145,47 +134,38 @@
     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;
     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.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;
     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.Fallback;
     import com.oracle.truffle.api.dsl.GenerateCached;
    @@ -194,22 +174,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 {
     
    @@ -221,116 +193,99 @@ 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,
    -                        @Bind Node inliningTarget,
    -                        @Cached PythonToNativeNode toSulongNode,
    -                        @Cached NativeToPythonTransferNode toJavaNode,
    -                        @CachedLibrary(limit = "1") InteropLibrary interopLibrary) {
    +        static Object doGeneric(Node inliningTarget, Object object, double arg,
    +                        @Cached PythonToNativeInternalNode toNativeNode,
    +                        @Cached NativeToPythonInternalNode toJavaNode,
    +                        @Cached PyObjectCheckFunctionResultNode checkFunctionResultNode) {
                 assert TypeNodes.NeedsNativeAllocationNode.executeUncached(object);
    +            NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_FLOAT_SUBTYPE_NEW);
    +            long result;
                 try {
    -                Object callable = CApiContext.getNativeSymbol(inliningTarget, getFunction());
    -                Object result = interopLibrary.execute(callable, toSulongNode.execute(object), arg);
    -                return toJavaNode.execute(result);
    -            } catch (UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
    -                throw shouldNotReachHere("C subtype_new function failed", e);
    +                result = ExternalFunctionInvoker.invokeFLOAT_SUBTYPE_NEW(callable.getAddress(), toNativeNode.execute(inliningTarget, object), arg);
    +            } catch (Throwable e) {
    +                throw CompilerDirectives.shouldNotReachHere(e);
                 }
    +            return checkFunctionResultNode.execute(PythonContext.get(inliningTarget), NativeCAPISymbol.FUN_FLOAT_SUBTYPE_NEW.getTsName(),
    +                            toJavaNode.executeTransfer(inliningTarget, result));
             }
         }
     
    -    @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),
    +                                toNativeNode.execute(inliningTarget, arg));
    +                return toJavaNode.executeTransfer(inliningTarget, result);
    +            } 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 {
    -
    -        @Child private PythonToNativeNode toNativeNode;
    +    @GenerateInline
    +    @GenerateCached(false)
    +    public abstract static class StringSubtypeNew extends Node {
     
    -        @Override
    -        protected final NativeCAPISymbol getFunction() {
    -            return NativeCAPISymbol.FUN_UNICODE_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 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),
    +                                toNativeNode.execute(inliningTarget, promotedArg));
    +                Reference.reachabilityFence(promotedArg);
    +                return toJavaNode.executeTransfer(inliningTarget, result);
    +            } catch (Throwable e) {
    +                throw CompilerDirectives.shouldNotReachHere(e);
                 }
    -            return execute(object, toNativeNode.execute(arg));
    -        }
    -
    -        public static StringSubtypeNew create() {
    -            return CExtNodesFactory.StringSubtypeNewNodeGen.create();
             }
         }
     
    -    @GenerateInline(true)
    -    @GenerateCached(false)
    -    @GenerateUncached(false)
    -    public abstract static class DictSubtypeNew extends Node {
    -        public abstract PDict execute(Node node, Object cls, PDict managedSide);
    -
    -        @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);
    -            CApiTransitions.writeNativeRefCount(nativeObject, MANAGED_REFCNT);
    -            CApiTransitions.createReference(nativeWrapper, nativeObject, false);
    -            return managedSide;
    -        }
    +    public static  T allocateNativePart(Node inliningTarget, Object cls, T managedSide) {
    +        assert !managedSide.isNative();
    +        assert EnsurePythonObjectNode.doesNotNeedPromotion(cls);
    +        long nativeObject;
    +        try {
    +            nativeObject = ExternalFunctionInvoker.invokePY_TYPE_GENERIC_NEW_RAW(CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW_RAW).getAddress(),
    +                            PythonToNativeInternalNode.executeUncached(cls, false), 0L, 0L);
    +        } catch (Throwable t) {
    +            throw CompilerDirectives.shouldNotReachHere(t);
    +        } finally {
    +            Reference.reachabilityFence(cls);
    +        }
    +        PythonContext context = PythonContext.get(inliningTarget);
    +        TransformExceptionFromNativeNode.executeUncached(context.getThreadState(context.getLanguage()), NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW_RAW.getTsName(), nativeObject == NULLPTR,
    +                        true);
    +        CApiTransitions.writeNativeRefCount(nativeObject, MANAGED_REFCNT);
    +        CApiTransitions.createReference(managedSide, nativeObject);
    +        assert managedSide.isNative();
    +        return managedSide;
         }
     
         // -----------------------------------------------------------------------------------------------------------------
    @@ -343,10 +298,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;
             }
    @@ -365,165 +319,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;
             }
     
    @@ -534,58 +384,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);
    +        public abstract TruffleString execute(Node inliningTarget, long charPtr, boolean copy);
     
             @Specialization
    -        static TruffleString doCStringWrapper(CStringWrapper cStringWrapper, @SuppressWarnings("unused") boolean copy) {
    -            return cStringWrapper.getString();
    -        }
    -
    -        @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);
             }
         }
     
    @@ -598,57 +421,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);
             }
         }
     
    @@ -747,8 +524,8 @@ static PComplex runGeneric(Node inliningTarget, Object value,
          * avoid eager and explicit conversion.
          */
         @ImportStatic(PythonUtils.class)
    -    @GenerateInline(inlineByDefault = true)
    -    @GenerateCached
    +    @GenerateInline
    +    @GenerateCached(false)
         @GenerateUncached
         public abstract static class CastToNativeLongNode extends PNodeWithContext {
             public abstract long execute(Node inliningTarget, boolean arg);
    @@ -804,77 +581,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);
    -                if (!pythonContext.hasCApiContext()) {
    -                    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 +598,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 +621,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 +682,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 @@ -989,19 +697,22 @@ public static long lookupNativeI64MemberInMRO(Object cls, CFields nativeMemberNa @GenerateUncached @GenerateInline(false) // footprint reduction 44 -> 26 public abstract static class LookupNativeI64MemberFromBaseNode extends Node { + @FunctionalInterface + public interface BuiltinCallback { + int apply(PythonBuiltinClassType t); + } public final long execute(Object cls, CFields nativeMemberName, HiddenAttr managedMemberName) { return execute(cls, nativeMemberName, managedMemberName, null); } - public abstract long execute(Object cls, CFields nativeMemberName, HiddenAttr managedMemberName, Function builtinCallback); + public abstract long execute(Object cls, CFields nativeMemberName, HiddenAttr managedMemberName, BuiltinCallback builtinCallback); @Specialization - static long doSingleContext(Object cls, CFields nativeMember, HiddenAttr managedMemberName, Function builtinCallback, + static long doSingleContext(Object cls, CFields nativeMember, HiddenAttr managedMemberName, BuiltinCallback builtinCallback, @Bind Node inliningTarget, @Cached GetBaseClassNode getBaseClassNode, @Cached HiddenAttr.ReadNode readAttrNode, - @Cached CStructAccess.ReadI64Node getTypeMemberNode, @Cached PyNumberAsSizeNode asSizeNode) { CompilerAsserts.partialEvaluationConstant(builtinCallback); @@ -1025,7 +736,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); @@ -1075,6 +786,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 { @@ -1107,173 +827,47 @@ 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 InlinedBranchProfile isSpecialSingletonProfile, + @Cached UpdateStrongRefNode updateRefNode) { + if (pointer == NULLPTR) { return; } + // Boxed primitive values do not have a native reference count. if (HandlePointerConverter.pointsToPyFloatHandle(pointer) || HandlePointerConverter.pointsToPyIntHandle(pointer)) { return; } - PythonNativeWrapper wrapper = toPythonWrapperNode.executeWrapper(pointer, false); - if (wrapper instanceof PythonAbstractObjectNativeWrapper objectWrapper) { - 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); - } + Object object = toPythonNode.execute(inliningTarget, pointer); + if (object instanceof PythonAbstractNativeObject) { + // Native objects use their native reference count and deallocator. + 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); } + } else if (CApiContext.isSpecialSingleton(object)) { + // Special singletons such as PNone are immortal handle-space objects. + isSpecialSingletonProfile.enter(inliningTarget); + } else if (object instanceof PythonObject pythonObject) { + // Managed Python objects keep their reference count in the wrapper. + isWrapperProfile.enter(inliningTarget); + 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); - } + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw CompilerDirectives.shouldNotReachHere("unexpected object for decref: " + object); } - // 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; } } @@ -1347,11 +941,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 @@ -1368,287 +957,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 @@ -1669,11 +984,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. @@ -1682,34 +993,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; @@ -1726,24 +1037,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 = NativeToPythonInternalNode.executeUncached(result, true); /* * We are more strict than CPython and require this to be a PythonModule object. This @@ -1759,16 +1061,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) { @@ -1785,86 +1087,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); + long mSize = readLongField(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); - } + // 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 - 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); - } + // 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;
    @@ -1875,34 +1164,31 @@ 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)) { - methodDoc = FromCharPointerNode.executeUncached(methodDocPtr, false); + long methodDocPtr = readStructArrayPtrField(methodDefPtr, element, PyMethodDef__ml_doc); + if (methodDocPtr != NULLPTR) { + methodDoc = FromCharPointerNode.executeUncached(methodDocPtr); } - 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); - PBuiltinFunction function = PFactory.createBuiltinFunction(language, methodName, null, PythonUtils.EMPTY_OBJECT_ARRAY, kwDefaults, flags, callTarget); - HiddenAttr.WriteNode.executeUncached(function, METHOD_DEF_PTR, methodDef); + MethodDescriptorWrapper sig = MethodDescriptorWrapper.fromMethodFlags(flags); + PRootNode rootNode = MethodDescriptorWrapper.getOrCreateRootNode(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, rootNode); + 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); + CFunctionDocUtils.writeDocAndTextSignature(function, methodName, methodDoc); return function; } @@ -1914,13 +1200,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; } } @@ -1928,93 +1211,99 @@ 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, - @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); + static PMemoryView fromNative(Node inliningTarget, PythonNativeObject buf, int flags, + @Cached PythonToNativeInternalNode toNativeNode, + @Cached NativeToPythonInternalNode asPythonObjectNode, + @Cached(inline = false) PyObjectCheckFunctionResultNode checkFunctionResultNode) { + long bufPointer = toNativeNode.execute(inliningTarget, 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.executeTransfer(inliningTarget, 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 && Double.isNaN(d)) { + return d; + } 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 f9d469fa97..93a5d225f9 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 @@ -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,107 +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.nodes.StringLiterals.T_EMPTY_STRING; +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.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.CreateNativeKwNamesTupleNodeGen; +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.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.NativeToPythonNode; +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.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.object.PythonBuiltinObject; import com.oracle.graal.python.builtins.objects.str.PString; 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.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.nativeaccess.NativeFunctionPointer; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; 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; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; @@ -151,16 +182,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,752 +200,338 @@ 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)}; } - /** - * On Windows, "long" is 32 bits, so that we might need to convert int to long for consistency. - */ @GenerateInline(false) - @GenerateUncached - public abstract static class FromLongNode extends CExtToJavaNode { - - @Specialization - static long doInt(int value) { - return value & 0xFFFFFFFFL; - } + public static final class ToNativeBorrowedNode extends CExtToNativeNode { - @Specialization - static long doLong(long value) { - return value; - } + @Child private EnsurePythonObjectNode ensurePythonObjectNode = EnsurePythonObjectNode.create(); + @Child private PythonToNativeNode toNative = PythonToNativeNode.create(); - @Fallback - static Object doOther(Object value) { - assert CApiTransitions.isBackendPointerObject(value); - return value; + @Override + public long execute(Object object) { + assert canBorrowDirectly(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 either be owned by a container object (e.g. a list) or be directly + * encodable as a tagged native handle. We still need to promote some objects 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.execute(promoted); } - @NeverDefault - public static FromLongNode create() { - return ExternalFunctionNodesFactory.FromLongNodeGen.create(); + private static boolean canBorrowDirectly(Object object) { + return object instanceof Integer || + object instanceof Long l && PInt.fitsInInt(l) || + object instanceof Float || + object instanceof Double d && PFloat.fitsInFloat(d) || + !(object instanceof Number || object instanceof TruffleString); } - public static FromLongNode getUncached() { - return ExternalFunctionNodesFactory.FromLongNodeGen.getUncached(); + @TruffleBoundary(allowInlining = true) + public static long executeUncached(Object object) { + Object promoted = EnsurePythonObjectNode.executeUncached(PythonContext.get(null), object, false); + return PythonToNativeInternalNode.executeUncached(promoted, false); } } - @GenerateInline(false) @GenerateUncached - public abstract static class FromUInt32Node extends CExtToJavaNode { - - @Specialization - static int doInt(int value) { - return value; - } - + @GenerateInline(false) + public abstract static class ToPythonStringNode extends CExtToJavaNode { @Specialization - static int doLong(long value) { - assert value < (1L << 32); - return (int) value; + static Object doIt(long pointer, + @Bind Node inliningTarget, + @Cached CastToTruffleStringNode castToStringNode, + @Cached NativeToPythonInternalNode nativeToPythonNode) { + Object result = nativeToPythonNode.execute(inliningTarget, pointer); + if (result == PNone.NO_VALUE) { + return result; + } + return castToStringNode.castKnownString(inliningTarget, result); } @NeverDefault - public static FromUInt32Node create() { - return ExternalFunctionNodesFactory.FromUInt32NodeGen.create(); + public static ToPythonStringNode create() { + return ToPythonStringNodeGen.create(); } - public static FromUInt32Node getUncached() { - return ExternalFunctionNodesFactory.FromUInt32NodeGen.getUncached(); + public static ToPythonStringNode getUncached() { + return ToPythonStringNodeGen.getUncached(); } } - @GenerateInline(false) - public abstract static class ToInt64Node extends CExtToNativeNode { + /** Enum of all slot wrapper functions in {@code typeobject.c}. */ + public enum PExternalFunctionWrapper implements NativeCExtSymbol { + GETATTR(GetAttrFuncRootNode.class, ExternalFunctionSignature.GETATTRFUNC), + SETATTR(SetAttrFuncRootNode.class, ExternalFunctionSignature.SETATTRFUNC), - @Specialization - static long doInt(int value) { - return value; - } + RICHCMP(RichCmpFuncRootNode.class, ExternalFunctionSignature.RICHCMPFUNC), - @Specialization - static long doLong(long value) { - return value; - } + // wrap_sq_setitem + SQ_SETITEM(SetItemRootNode.class, ExternalFunctionSignature.SSIZEOBJARGPROC), - @Fallback - static Object doOther(Object value) { - assert CApiTransitions.isBackendPointerObject(value); - return value; - } + // wrap_unaryfunc + UNARYFUNC(MethUnaryFunc.class, ExternalFunctionSignature.UNARYFUNC), - @NeverDefault - public static ToInt64Node create() { - return ExternalFunctionNodesFactory.ToInt64NodeGen.create(); - } - } + // wrap_binaryfunc + BINARYFUNC(MethBinaryRoot.class, ExternalFunctionSignature.BINARYFUNC), - @GenerateInline(false) - public abstract static class ToInt32Node extends CExtToNativeNode { + // wrap_binaryfunc_l + BINARYFUNC_L(MethBinaryRoot.class, ExternalFunctionSignature.BINARYFUNC), - @Specialization - static int doInt(int value) { - return value; - } + // wrap_binaryfunc_r + BINARYFUNC_R(MethBinaryRoot.class, ExternalFunctionSignature.BINARYFUNC), - @NeverDefault - public static ToInt32Node create() { - return ExternalFunctionNodesFactory.ToInt32NodeGen.create(); - } - } + // wrap_ternaryfunc + TERNARYFUNC(MethTernaryFuncRoot.class, ExternalFunctionSignature.TERNARYFUNC, 1), - @GenerateInline(false) - public static final class ToNativeBorrowedNode extends CExtToNativeNode { + // wrap_ternaryfunc_r + TERNARYFUNC_R(MethTernaryFuncRoot.class, ExternalFunctionSignature.TERNARYFUNC, 1), - @Child private PythonToNativeNode toNative = PythonToNativeNodeGen.create(); + // richcmp_lt + LT(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC), - @Override - public Object execute(Object object) { - assert (object instanceof Double && Double.isNaN((double) object)) || !(object instanceof Number || object instanceof TruffleString); - return toNative.execute(object); - } - } + // richcmp_le + LE(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC), - @GenerateUncached - @GenerateInline(false) - public abstract static class ToPythonStringNode extends CExtToJavaNode { - @Specialization - static Object doIt(Object object, - @Bind Node inliningTarget, - @Cached CastToTruffleStringNode castToStringNode, - @Cached NativeToPythonNode nativeToPythonNode) { - Object result = nativeToPythonNode.execute(object); - if (result == PNone.NO_VALUE) { - return result; - } - return castToStringNode.castKnownString(inliningTarget, result); - } + // richcmp_eq + EQ(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC), - @NeverDefault - public static ToPythonStringNode create() { - return ExternalFunctionNodesFactory.ToPythonStringNodeGen.create(); - } + // richcmp_ne + NE(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC), - public static ToPythonStringNode getUncached() { - return ExternalFunctionNodesFactory.ToPythonStringNodeGen.getUncached(); - } - } + // richcmp_gt + GT(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC), - /** - * Enum of well-known function and slot signatures. The integer values must stay in sync with - * the definition in {code capi.h}. - */ - 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; + // richcmp_ge + GE(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC), - PExternalFunctionWrapper(int value, int numDefaults, ArgDescriptor returnValue, ArgDescriptor... arguments) { - this.value = value; - this.returnValue = returnValue; - this.arguments = arguments; + // wrap_next + ITERNEXT(IterNextFuncRootNode.class, ExternalFunctionSignature.UNARYFUNC), - 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_inquirypred + INQUIRYPRED(MethInquiryRoot.class, ExternalFunctionSignature.INQUIRY), - PExternalFunctionWrapper(int value, ArgDescriptor returnValue, ArgDescriptor... arguments) { - this(value, 0, returnValue, arguments); - } + // wrap_sq_delitem + SQ_DELITEM(SqDelItemRootNode.class, ExternalFunctionSignature.SSIZEOBJARGPROC), - private final int value; + // wrap_sq_item + SQ_ITEM(GetItemRootNode.class, ExternalFunctionSignature.SSIZEARGFUNC), - static { - for (var e : VALUES) { - assert BY_ID[e.value] == null; - BY_ID[e.value] = e; - } - } + // wrap_init + INIT(MethInitRoot.class, ExternalFunctionSignature.INITPROC), - static PExternalFunctionWrapper fromValue(int value) { - return value >= 0 && value < BY_ID.length ? BY_ID[value] : null; - } + // wrap_hashfunc + HASHFUNC(MethLenfuncRoot.class, ExternalFunctionSignature.HASHFUNC), - 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_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 PRootNode getOrCreateRootNode(PExternalFunctionWrapper sig, PythonLanguage language, TruffleString name) { + return language.createCachedExternalFunWrapperRootNode(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} + * {@link PRootNode} * @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)); + PRootNode rootNode = getOrCreateRootNode(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, rootNode, slot); } - return result; + return PFactory.createWrapperDescriptor(language, name, type, defaults, kwDefaults, 0, rootNode, slot, sig); } + @Override public String getName() { return name(); } - 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); + @Retention(RetentionPolicy.SOURCE) + @Target(ElementType.METHOD) + public @interface InvokeExternalFunction { + ExternalFunctionSignature value(); - @NeverDefault - static CheckFunctionResultNode createCheckResultNode(PExternalFunctionWrapper provider) { - CheckFunctionResultNode node = provider.createCheckFunctionResultNode(); - return node != null ? node : DefaultCheckFunctionResultNodeGen.create(); - } - - 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 +567,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; - - protected boolean seenNativeArgsTupleStorage; + /** + * 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; - 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(); - } + final CApiTiming timing; - @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}; + 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 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 GetThreadStateNode ensureGetThreadStateNode() { + if (getThreadStateNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + getThreadStateNode = insert(GetThreadStateNode.create()); } - releaseNativeWrapperNode.execute(cArguments[2]); + return getThreadStateNode; } - @Override - public Signature getSignature() { - return SIGNATURE; + final Object nativeToPython(PythonContext context, long lresult) { + if (nativeToPythonReturnNode == null || checkResultNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + nativeToPythonReturnNode = insert(NativeToPythonReturnNode.create()); + checkResultNode = insert(PyObjectCheckFunctionResultNodeGen.create()); + } + return checkResultNode.execute(context, name, nativeToPythonReturnNode.execute(lresult)); } } - public static final class MethVarargsRoot extends MethodDescriptorRoot { - private static final Signature SIGNATURE = createSignature(false, 1, tsArray("self"), true, true); - @Child private ReadVarArgsNode readVarargsNode; - @Child private CreateArgsTupleNode createArgsTupleNode; - @Child private ReleaseNativeSequenceStorageNode freeNode; + /** + * 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 boolean seenNativeArgsTupleStorage; + private final BranchProfile exceptionProfile = BranchProfile.create(); - public MethVarargsRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) { - super(language, name, isStatic, provider); - this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex()); - this.createArgsTupleNode = CreateArgsTupleNodeGen.create(); - this.freeNode = ReleaseNativeSequenceStorageNodeGen.create(); + WrapperDescriptorRoot(PythonLanguage language, TruffleString name, @SuppressWarnings("unused") PExternalFunctionWrapper wrapper) { + super(language, name, false); } - @Override - protected Object[] prepareCArguments(VirtualFrame frame) { - Object self = readSelf(frame); - Object[] args = readVarargsNode.execute(frame); - return new Object[]{self, createArgsTupleNode.execute(getLanguage(PythonLanguage.class), args, seenNativeArgsTupleStorage)}; + protected final void transformExceptionFromNative() { + transformExceptionFromNative(true); } - @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; + protected final void transformExceptionFromNative(boolean strict) { + exceptionProfile.enter(); + transformExceptionFromNative(PythonContext.get(this), strict); + if (strict) { + throw CompilerDirectives.shouldNotReachHere(); } } - static boolean releaseArgsTuple(Object argsTupleObject, ReleaseNativeSequenceStorageNode freeNode, boolean eagerNativeStorage) { - if (!PythonContext.get(freeNode).isNativeAccessAllowed()) { - return false; + @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().execute(lresult); + if (result == PNone.NO_VALUE) { + transformExceptionFromNative(); } + return result; + } + } + + 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 PythonToNativeNode argsTupleToNativeNode; + @Child private CreateNativeArgsTupleNode createNativeArgsTupleNode; + @Child private ReleaseNativeArgsTupleNode freeNode; + + @CompilationFinal private boolean seenNativeArgsTupleStorage; + + public MethVarargsRoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) { + super(language, name, isStatic, provider); + this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex()); + this.argsTupleToNativeNode = PythonToNativeNode.create(); + this.createNativeArgsTupleNode = CreateNativeArgsTupleNodeGen.create(); + this.freeNode = ReleaseNativeArgsTupleNodeGen.create(); + } + + @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.execute(managedArgsTuple); + } + 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,40 +730,286 @@ 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); + + @Child private ReadVarArgsNode readVarargsNode; + @Child private ReadVarKeywordsNode readKwargsNode; + @Child private CreateNativeArgsTupleNode createNativeArgsTupleNode; + @Child private ReleaseNativeArgsTupleNode freeNode; + @Child private CalleeContext calleeContext; + @Child private BoundaryCallData boundaryCallData; + + @Child private PythonToNativeNode selfToNativeNode; + @Child private PythonToNativeNode argsToNativeNode; + @Child private PythonToNativeNode kwargsToNativeNode; + + @CompilationFinal private boolean seenNativeArgsTupleStorage; - public MethNewRoot(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) { - Object methodSelf = 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); + + 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.execute(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.execute(self), argsTuplePtr, kwargsToNativeNode.execute(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() { + return SIGNATURE; + } + } + + /** 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); + + 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 readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) { + Object self = readSelf(frame); + return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self)); + } + + @Override + 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 + public Signature getSignature() { + return SIGNATURE; + } + } + + @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.execute(managedArgsTuple); + } + 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}; + 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); + } } } - public static final class MethInquiryRoot extends MethodDescriptorRoot { - private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self"), true, false); + @CApiWrapperDescriptor(value = CALL) + abstract static class MethCallRoot extends MethNewOrCallRoot { - public MethInquiryRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) { - super(language, name, isStatic, provider); + 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[] prepareCArguments(VirtualFrame frame) { - return new Object[]{readSelf(frame)}; + 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.execute(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; + + @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 void postprocessCArguments(VirtualFrame frame, Object[] cArguments) { - ensureReleaseNativeWrapperNode().execute(cArguments[0]); + 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.execute(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 @@ -1128,21 +1018,46 @@ 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_inquirypred}. */ + @CApiWrapperDescriptor(value = INQUIRYPRED) + public abstract static class MethInquiryRoot extends WrapperDescriptorRoot { + 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); + 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[] prepareCArguments(VirtualFrame frame) { - return new Object[]{readSelf(frame), PNone.NO_VALUE}; + 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 - protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) { - ensureReleaseNativeWrapperNode().execute(cArguments[0]); + 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); + } + + @Override + protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) { + Object self = readSelf(frame); + assert EnsurePythonObjectNode.doesNotNeedPromotion(self); + return invokeExternalFunction(frame, boundFunction, self, NULLPTR); } @Override @@ -1151,27 +1066,60 @@ public Signature getSignature() { } } - public static final class MethORoot extends MethodDescriptorRoot { + 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.execute(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, PExternalFunctionWrapper provider) { + 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[] prepareCArguments(VirtualFrame frame) { + protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) { Object self = readSelf(frame); - Object arg = readArgNode.execute(frame); - return new Object[]{self, arg}; - } - - @Override - protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) { - ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode(); - releaseNativeWrapperNode.execute(cArguments[0]); - releaseNativeWrapperNode.execute(cArguments[1]); + Object arg = ensurePythonObject(readArgNode.execute(frame)); + try { + return invokeExternalFunction(frame, boundFunction, self, argToNativeNode.execute(arg)); + } finally { + Reference.reachabilityFence(arg); + } } @Override @@ -1180,44 +1128,86 @@ 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; + + @Child private PythonToNativeNode argToNativeNode; + @Child private PythonToNativeNode kwNamesToNativeNode; + @Child private CreateNativeKwNamesTupleNode createNativeKwNamesTupleNode; + @Child private ReleaseNativeArgsTupleNode releaseNativeKwNamesTupleNode; - public MethFastcallWithKeywordsRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) { + 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; + assert createNativeKwNamesTupleNode == null; + assert releaseNativeKwNamesTupleNode == 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); + Object[] fastcallKwnames = null; + long kwnamesTuplePtr = NULLPTR; + 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]; + 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()); + } + kwnamesTuplePtr = createNativeKwNamesTupleNode.execute(context, 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.execute(self), nativeFastcallArgs, args.length, kwnamesTuplePtr); + return nativeToPython(context, l); + } finally { + MethFastcallRoot.freeFastcallArgsArray(nativeFastcallArgs); + if (fastcallKwnames != null) { + MethVarargsRoot.postprocessArgsTuple(null, kwnamesTuplePtr, fastcallKwnames, releaseNativeKwNamesTupleNode); } - kwnamesTuple = PFactory.createTuple(PythonLanguage.get(this), fastcallKwnames); + Reference.reachabilityFence(self); + Reference.reachabilityFence(fastcallArgs); + Reference.reachabilityFence(fastcallKwnames); } - 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()); + createNativeKwNamesTupleNode = insert(CreateNativeKwNamesTupleNodeGen.create()); + releaseNativeKwNamesTupleNode = insert(ReleaseNativeArgsTupleNodeGen.create()); } @Override @@ -1226,43 +1216,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.execute(self), argToNativeNode.execute(cls), nativeFastcallArgs, args.length, kwNamesToNativeNode.execute(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 +1294,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; - public MethFastcallRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) { + @Child private PythonToNativeNode argToNativeNode; + + 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.execute(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.execute(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 +1382,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 +1414,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 +1453,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 - protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) { - ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode(); - releaseNativeWrapperNode.execute(cArguments[0]); - free.free(cArguments[1]); - releaseNativeWrapperNode.execute(cArguments[2]); + 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 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 +1530,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 +1600,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 +1672,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 +1704,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 +1735,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 +1799,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,125 +1851,184 @@ 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().execute(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); + if (readClosureNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + readClosureNode = insert(createReadClosureNode(SIGNATURE)); + } + long closure = (long) readClosureNode.execute(frame); + return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, closure)); } - Object[] getArguments(Object arg0, Object arg1, Object arg2) { - return new Object[]{arg0, arg1, arg2}; + 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 - protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) { - ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode(); - releaseNativeWrapperNode.execute(cArguments[0]); - releaseNativeWrapperNode.execute(cArguments[1]); - releaseNativeWrapperNode.execute(cArguments[2]); + public Signature getSignature() { + return SIGNATURE; + } + } + + /** + * Wrapper root node for C function type {@code setter}. + */ + @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; + + 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 + 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; } @Override public Signature getSignature() { return SIGNATURE; } - } - - /** - * Wrapper root node for native reverse power function (with an optional third argument). - */ - static final class MethRPowRootNode extends MethPowRootNode { - - MethRPowRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) { - super(language, name, provider); - } - @Override - Object[] getArguments(Object arg0, Object arg1, Object arg2) { - return new Object[]{arg1, arg0, arg2}; + private ReadIndexedArgumentNode ensureReadArgNode() { + if (readArgNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + readArgNode = insert(ReadIndexedArgumentNode.create(1)); + } + return readArgNode; } } - /** - * 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; + /** 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); - MethRichcmpOpRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider, int op) { - super(language, name, false, provider); - this.readArgNode = ReadIndexedArgumentNode.create(1); - this.op = op; + public MethLenfuncRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) { + super(language, name, provider); } - @Override - protected Object[] prepareCArguments(VirtualFrame frame) { - Object self = readSelf(frame); - Object arg = readArgNode.execute(frame); - return new Object[]{self, arg, op}; - } + @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 +2037,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 +2151,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); - - @Child private ReadIndexedArgumentNode readArgNode; + /** 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; - 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; @@ -1948,149 +2230,176 @@ public static EagerTupleState getUncached() { } } + private static void internStringArgs(PythonContext context, Object[] args) { + for (int i = 0; i < args.length; i++) { + assert args[i] instanceof TruffleString; + args[i] = internStringArg(context, (TruffleString) args[i]); + } + } + + private static PString internStringArg(PythonContext context, TruffleString arg) { + PString pString = PFactory.createString(context.getLanguage(), PythonUtils.internString(arg)); + return context.getCApiContext().getPstringInterningCache().intern(pString, s -> s); + } + /** - * 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 abstract static class CreateNativeArgsTupleNode extends Node { + private static final CApiTiming TIMING_invokeTypeGenericAlloc = CApiTiming.create(true, "PyType_GenericAlloc"); - public final PTuple execute(Node inliningTarget, PythonLanguage language, Object[] args, EagerTupleState state) { - return execute(language, args, state.isEager(inliningTarget)); - } + static final TruffleLogger LOGGER = CApiContext.getLogger(CreateNativeArgsTupleNode.class); - @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) { + public abstract long execute(PythonContext context, Object[] args); - for (int i = 0; i < cachedLen; i++) { - args[i] = materializePrimitiveNodes[i].execute(language, args[i]); + @Specialization + static long doGeneric(PythonContext context, Object[] args, + @Bind Node inliningTarget, + @Cached EnsurePythonObjectNode materializePrimitiveNode, + @Cached PythonToNativeInternalNode pythonToNativeNode) { + if (!context.isNativeAccessAllowed()) { + throw CompilerDirectives.shouldNotReachHere(); } - return PFactory.createTuple(language, 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, - @Bind Node inliningTarget, - @Cached("args.length") int cachedLen, - @Cached("createMaterializeNodes(args.length)") MaterializePrimitiveNode[] materializePrimitiveNodes, - @Exclusive @Cached StorageToNativeNode storageToNativeNode) { + int n = args.length; - for (int i = 0; i < cachedLen; i++) { - args[i] = materializePrimitiveNodes[i].execute(language, args[i]); - } - return PFactory.createTuple(language, storageToNativeNode.execute(inliningTarget, args, cachedLen, true)); - } + assert (GetTypeFlagsNode.executeUncached(PythonBuiltinClassType.PTuple) & TypeFlags.HAVE_GC) != 0; - @Specialization(replaces = {"doCachedLen", "doCachedLenEagerNative"}) - static PTuple doGeneric(PythonLanguage language, Object[] args, boolean eagerNative, - @Bind Node inliningTarget, - @Cached MaterializePrimitiveNode materializePrimitiveNode, - @Exclusive @Cached StorageToNativeNode storageToNativeNode) { + 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), n); + + long obItem = CStructAccess.getFieldPtr(op, CFields.PyTupleObject__ob_item); - int n = args.length; for (int i = 0; i < n; i++) { - args[i] = materializePrimitiveNode.execute(language, args[i]); + Object promoted = materializePrimitiveNode.execute(context, args[i], false); + args[i] = promoted; + writePtrArrayElement(obItem, i, pythonToNativeNode.executeNewRef(inliningTarget, promoted)); } - 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(); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine(PythonUtils.formatJString("Created native args tuple %x (size=%d)", op, n)); } - return materializePrimitiveNodes; + + assert !HandlePointerConverter.pointsToPyHandleSpace(op); + assert PyObjectGCTrackNode.isGcTracked(op); + return op; } } + /** + * Allocates a native tuple and initializes it with interned string objects. This is used for + * FASTCALL keyword-name tuples where the source names are known to be {@link TruffleString} + * instances. + */ @GenerateInline(false) - @ImportStatic(PythonUtils.class) - abstract static class ReleaseNativeSequenceStorageNode extends Node { + public abstract static class CreateNativeKwNamesTupleNode extends Node { - abstract void execute(NativeSequenceStorage storage); + public abstract long execute(PythonContext context, Object[] args); - @Specialization(guards = {"storage.length() == cachedLen", "cachedLen <= 8"}, limit = "1") - @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL) - static void doObjectCachedLen(NativeObjectSequenceStorage storage, + @Specialization + static long doGeneric(PythonContext context, Object[] args, @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); + @Cached PythonToNativeInternalNode pythonToNativeNode) { + if (!context.isNativeAccessAllowed()) { + throw CompilerDirectives.shouldNotReachHere(); } - // 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); + int n = args.length; + + assert (GetTypeFlagsNode.executeUncached(PythonBuiltinClassType.PTuple) & TypeFlags.HAVE_GC) != 0; + + PythonBuiltinClass argsTupleClass = context.lookupType(PythonBuiltinClassType.PTuple); + NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, FUN_PY_TYPE_GENERIC_ALLOC); + long op = ExternalFunctionInvoker.invokeTYPE_GENERIC_ALLOC(null, CreateNativeArgsTupleNode.TIMING_invokeTypeGenericAlloc, context.ensureNativeContext(), + BoundaryCallData.getUncached(), context.getThreadState(context.getLanguage(inliningTarget)), callable, + pythonToNativeNode.execute(inliningTarget, argsTupleClass), n); + + long obItem = CStructAccess.getFieldPtr(op, CFields.PyTupleObject__ob_item); + + for (int i = 0; i < n; i++) { + assert args[i] instanceof TruffleString; + PString promoted = internStringArg(context, (TruffleString) args[i]); + assert EnsurePythonObjectNode.doesNotNeedPromotion(promoted); + args[i] = promoted; + long nativeString = pythonToNativeNode.executeNewRef(inliningTarget, promoted); + CApiTransitions.setGraalPyUnicodeObjectInterned(HandlePointerConverter.pointerToStub(nativeString), CApiTransitions.GRAALPY_UNICODE_INTERN_STATE_INTERNED); + writePtrArrayElement(obItem, i, nativeString); } - // in this case, the runtime still exclusively owns the memory - freeNode.free(storage.getPtr()); + + if (CreateNativeArgsTupleNode.LOGGER.isLoggable(Level.FINE)) { + CreateNativeArgsTupleNode.LOGGER.fine(PythonUtils.formatJString("Created native string args tuple %x (size=%d)", op, n)); + } + + 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); + public abstract static class ReleaseNativeArgsTupleNode extends Node { + private static final CApiTiming TIMING_invokePyDealloc = CApiTiming.create(true, "_Py_Dealloc"); - // NOTE: Booleans don't need to be materialized because they are singletons. + public abstract void execute(long argsTuplePtr, Object[] managedArgs); @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); - } - - @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 +2407,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,72 +2451,24 @@ 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) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.StopIteration); } else { - throw PException.fromObject(currentException, inliningTarget, false); - } - } - return result; - } - } - - /** - * 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); + throw PException.fromObjectFixUncachedLocation(currentException, inliningTarget, false); } } - transformExceptionFromNativeNode.execute(inliningTarget, state, name, ret < 0, true); return result; } } @@ -2297,78 +2485,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..d1352bbe9d --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionSignature.java @@ -0,0 +1,292 @@ +/* + * 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.ConstCharPtr; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstPyLongObject; +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.INT8_T_PTR; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INTPTR_T_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.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.PY_BUFFER_PTR; +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.PyObject; +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.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.PyThreadStatePtr; +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.VA_LIST; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.visitproc; +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 *, void *, void *); + CAPIINIT(false, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer), + // PyThreadState **GraalPyPrivate_InitThreadStateCurrent(PyThreadState *tstate) + INIT_THREAD_STATE_CURRENT(true, PyThreadStatePtr, PyThreadState), + // void GraalPyPrivate_LogImpl(int, const char *, va_list); + LOG_IMPL(true, Void, Int, ConstCharPtr, VA_LIST), + // 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), + // uintptr_t GraalPyPrivate_Long_lv_tag(const PyLongObject *); + LONG_LV_TAG(true, UINTPTR_T, ConstPyLongObject), + // 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, visitproc, 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(PyObject **, int); + OBJECT_ARRAY_RELEASE(true, Void, PyObjectPtr, 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, PY_CAPSULE_DESTRUCTOR), + // Py_ssize_t GraalPyPrivate_BulkDealloc(intptr_t *, int64_t); + BULK_DEALLOC(true, Py_ssize_t, INTPTR_T_PTR, INT64_T), + // Py_ssize_t GraalPyPrivate_BulkDeallocOnShutdown(intptr_t *, int64_t); + SHUTDOWN_BULK_DEALLOC(true, Py_ssize_t, INTPTR_T_PTR, INT64_T), + // size_t GraalPyPrivate_GetCurrentRSS(void); + GET_CURRENT_RSS(true, SIZE_T), + // void GraalPyPrivate_ReleaseBuffer(Py_buffer *); + GRAALPY_RELEASE_BUFFER(true, Void, PY_BUFFER_PTR), + // void GraalPyPrivate_MMap_InitBufferProtocol(PyObject *); + MMAP_INIT_BUFFERPROTOCOL(true, Void, PyObject), + // PyObject *GraalPyPrivate_Exception_SubtypeNew(PyTypeObject *, PyObject *); + EXCEPTION_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, PyObject), + // PyObject *GraalPyPrivate_Bytes_SubtypeNew(PyTypeObject *, int8_t *, Py_ssize_t); + BYTES_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, INT8_T_PTR, 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, visitproc, 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(); + } + + 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..571a88e9c6 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/MethodDescriptorWrapper.java @@ -0,0 +1,217 @@ +/* + * 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.TruffleLogger; +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 ArgDescriptor getReturnValue() { + return returnValue; + } + + @Override + public ArgDescriptor[] getArguments() { + return arguments; + } + + private static final TruffleLogger LOGGER = CApiContext.getLogger(MethodDescriptorWrapper.class); + + @TruffleBoundary + static PRootNode getOrCreateRootNode(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.createCachedExternalFunWrapperRootNode(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 PRootNode} + * @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; + } + + PRootNode rootNode = getOrCreateRootNode(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, rootNode); + } + + /** + * 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 a842633b53..673218f930 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,20 +40,10 @@ */ 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 java.util.Objects; + 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.CompilerDirectives.CompilationFinal; @@ -61,108 +51,69 @@ 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_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_NO_OP_CLEAR("GraalPyPrivate_NoOpClear", ExternalFunctionSignature.NO_OP_CLEAR), + FUN_NO_OP_TRAVERSE("GraalPyPrivate_NoOpTraverse", ExternalFunctionSignature.NO_OP_TRAVERSE), /* 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_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), + FUN_GRAALPY_PRIVATE_LOG_IMPL("GraalPyPrivate_LogImpl", ExternalFunctionSignature.LOG_IMPL), + FUN_GET_FINALIZE_CAPI_POINTER("GraalPyPrivate_GetFinalizeCApiPointer", ExternalFunctionSignature.GETFINALIZECAPIPOINTER), + FUN_LONG_LV_TAG("GraalPyPrivate_Long_lv_tag", ExternalFunctionSignature.LONG_LV_TAG), /* 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) { - 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(); - } - - NativeCAPISymbol(String name) { + NativeCAPISymbol(String name, ExternalFunctionSignature signature) { this.name = name; this.tsName = toTruffleStringUncached(name); - this.signature = null; + this.signature = Objects.requireNonNull(signature); } @Override @@ -170,7 +121,6 @@ public String getName() { return name; } - @Override public TruffleString getTsName() { return tsName; } @@ -179,8 +129,16 @@ 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.getReturnValue(); + } + + @Override + public ArgDescriptor[] getArguments() { + 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 08fc5d4735..fda55ba29e 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 @@ -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,34 +40,42 @@ */ 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.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.PythonToNativeNode; -import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNodeGen; -import com.oracle.graal.python.builtins.objects.cext.common.NativePointer; +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.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.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.interop.InteropLibrary; +import com.oracle.truffle.api.TruffleLogger; /** * Emulates CPython's {@code PyThreadState} struct. *

    - * This wrapper does intentionally not implement {@link InteropLibrary#isPointer(Object)}, - * {@link InteropLibrary#asPointer(Object)}, and {@link InteropLibrary#toNative(Object)} because the - * factory method {@link #getOrCreateNativeThreadState(PythonLanguage, PythonContext)} will already - * return the appropriate pointer object that implements that. + * This wrapper intentionally does not expose pointer conversion because the factory method + * {@link #getOrCreateNativeThreadState(PythonLanguage, PythonContext)} returns the appropriate + * native pointer. *

    */ -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; @@ -75,68 +83,165 @@ 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(threadState); - 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); + 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 nativeWrapper.replacement; + return pointer; } - public static Object getNativeThreadState(PythonThreadState threadState) { - PThreadState nativeWrapper = threadState.getNativeWrapper(); - if (nativeWrapper != null) { - return nativeWrapper.replacement; + @TruffleBoundary + public static PDict getOrCreateThreadStateDict(PythonContext context, PythonThreadState threadState) { + /* + * C API initialization must be finished at that time. This implies that there is already a + * native thread state. + */ + assert context.getCApiState() == CApiState.INITIALIZED; + + long nativeThreadState = threadState.getNativePointer(); + assert nativeThreadState != NULLPTR; + + PDict threadStateDict = threadState.getDict(); + if (threadStateDict != null) { + assert PythonToNativeInternalNode.executeUncached(threadStateDict, false) == CStructAccess.readPtrField(nativeThreadState, CFields.PyThreadState__dict); + return threadStateDict; } - return null; - } - public PythonThreadState getThreadState() { - return (PythonThreadState) getDelegate(); + threadStateDict = PFactory.createDict(context.getLanguage()); + threadState.setDict(threadStateDict); + assert CStructAccess.readPtrField(nativeThreadState, CFields.PyThreadState__dict) == NULLPTR; + CStructAccess.writePtrField(nativeThreadState, CFields.PyThreadState__dict, PythonToNativeInternalNode.executeUncached(threadStateDict, false)); + + return threadStateDict; } + /** + * This method runs on a critical bootstrap path when creating the native thread state. It may + * execute while the C API state is still INITIALIZING and before the current thread has + * installed its native 'tstate_current' TLS slot. So, this code must stay very restricted: only + * use bootstrap-safe allocation and raw struct writes here. + * + * In particular, do not introduce conversions such as PythonToNative(NewRef)Node or any other + * code paths that may poll the native reference queue, materialize additional native wrappers, + * or otherwise assume that the native thread state is already fully initialized. + */ @TruffleBoundary - private static long allocateCLayout(PythonThreadState threadState) { - PythonToNativeNode toNative = PythonToNativeNodeGen.getUncached(); - - long ptr = CStructAccess.AllocateNode.allocUncachedPointer(CStructs.PyThreadState.size()); - CStructAccess.WritePointerNode writePtrNode = CStructAccess.WritePointerNode.getUncached(); + private static long allocateCLayout() { + long ptr = CStructAccess.allocate(CStructs.PyThreadState); PythonContext pythonContext = PythonContext.get(null); - PDict threadStateDict = threadState.getDict(); - if (threadStateDict == null) { - threadStateDict = PFactory.createDict(pythonContext.getLanguage()); - threadState.setDict(threadStateDict); - } - writePtrNode.write(ptr, CFields.PyThreadState__dict, toNative.execute(threadStateDict)); + /* + * 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. + */ + 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 bytesCharacters = callocPtrArray(256); + long deallocatingState = CStructAccess.getFieldPtr(ptr, CFields.PyThreadState__graalpy_deallocating); + long deallocating = mallocPtrArray(GRAALPY_DEALLOC_STACK_INITIAL_CAPACITY); + long singletons = CStructAccess.getFieldPtr(ptr, CFields.PyThreadState__singletons); + CStructAccess.writePtrField(ptr, CFields.PyThreadState__small_ints, smallInts); + CStructAccess.writePtrField(singletons, CFields.GraalPySingletons__bytes_characters, bytesCharacters); + 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 void clearSingletons(PythonThreadState threadState) { + long nativeCompanion = threadState.getNativePointer(); + if (nativeCompanion == UNINITIALIZED || nativeCompanion == NATIVE_POINTER_FREED) { + return; + } + long singletons = CStructAccess.getFieldPtr(nativeCompanion, CFields.PyThreadState__singletons); + long bytesCharacters = CStructAccess.readPtrField(singletons, CFields.GraalPySingletons__bytes_characters); + CStructAccess.writePtrField(singletons, CFields.GraalPySingletons__tuple_empty, NULLPTR); + CStructAccess.writePtrField(singletons, CFields.GraalPySingletons__bytes_empty, NULLPTR); + assert bytesCharacters != NULLPTR; + // Drop singleton roots before the handle table frees the corresponding stubs. + for (int i = 0; i < 256; i++) { + writePtrArrayElement(bytesCharacters, i, NULLPTR); + } + } + + 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) { + dispose(threadState, true); + } + + @TruffleBoundary + public static void dispose(PythonThreadState threadState, boolean markShuttingDown) { + long nativeCompanion = threadState.getNativePointer(); + if (nativeCompanion == UNINITIALIZED || nativeCompanion == NATIVE_POINTER_FREED) { + return; + } + + assert !HandlePointerConverter.pointsToPyHandleSpace(nativeCompanion); + if (markShuttingDown) { + threadState.clearNativePointer(); + } else { + threadState.resetNativePointerAfterDetach(); + } + + long deallocatingState = CStructAccess.getFieldPtr(nativeCompanion, CFields.PyThreadState__graalpy_deallocating); + long deallocatingItems = CStructAccess.readPtrField(deallocatingState, CFields.GraalPyDeallocState__items); + if (deallocatingItems != NULLPTR) { + NativeMemory.free(deallocatingItems); + } + long singletons = CStructAccess.getFieldPtr(nativeCompanion, CFields.PyThreadState__singletons); + long bytesCharacters = CStructAccess.readPtrField(singletons, CFields.GraalPySingletons__bytes_characters); + assert bytesCharacters != NULLPTR; + NativeMemory.free(bytesCharacters); + + // 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..6b1969a69a 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,21 @@ 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.annotations.CApiUpcallTarget; +import com.oracle.graal.python.annotations.NativeSimpleType; 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.PythonToNativeNewRefNode; -import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeNode; +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.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 +63,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.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.POINTER, NativeSimpleType.POINTER); + private static final NativeSignature SIGNATURE_2_ARG = NativeSignature.create(NativeSimpleType.POINTER, NativeSimpleType.POINTER, NativeSimpleType.POINTER); + private static final NativeSignature SIGNATURE_3_ARG = NativeSignature.create(NativeSimpleType.POINTER, NativeSimpleType.POINTER, NativeSimpleType.POINTER, + NativeSimpleType.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().findVirtual(PyCFunctionUnaryWrapper.class, + "executeUnary", MethodType.methodType(long.class, long.class)); + HANDLE_BINARY = MethodHandles.lookup().findVirtual(PyCFunctionBinaryWrapper.class, + "executeBinary", MethodType.methodType(long.class, long.class, long.class)); + HANDLE_VARARGS = MethodHandles.lookup().findVirtual(PyCFunctionVarargsWrapper.class, + "executeVarargs", MethodType.methodType(long.class, long.class, long.class)); + HANDLE_KEYWORDS = MethodHandles.lookup().findVirtual(PyCFunctionKeywordsWrapper.class, + "executeKeywords", MethodType.methodType(long.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 +129,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 +139,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 +152,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 +191,147 @@ 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 { + public 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); - } + @CApiUpcallTarget + @SuppressWarnings({"try", "unused"}) + public long executeUnary(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, + Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false); + Object[] pArgs = CreateArgumentsNode.executeUncached(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 result = SimpleIndirectInvokeNode.executeUncached(callTarget, pArgs); + return PythonToNativeInternalNode.executeNewRefUncached(result); } catch (Throwable t) { throw checkThrowableBeforeNative(t, 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); } } - @Override - protected String getSignature() { - return "(POINTER):POINTER"; - } - @Override protected String getFlagsRepr() { return "METH_NOARGS"; } } - @ExportLibrary(InteropLibrary.class) - static final class PyCFunctionBinaryWrapper extends PyCFunctionWrapper { + public 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); - } + @CApiUpcallTarget + @SuppressWarnings({"try", "unused"}) + public long executeBinary(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, + Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false); + Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false); + Object[] pArgs = CreateArgumentsNode.executeUncached(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 result = SimpleIndirectInvokeNode.executeUncached(callTarget, pArgs); + return PythonToNativeInternalNode.executeNewRefUncached(result); } catch (Throwable t) { throw checkThrowableBeforeNative(t, 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); } } - @Override - protected String getSignature() { - return "(POINTER,POINTER):POINTER"; - } - @Override protected String getFlagsRepr() { return "METH_O"; } } - @ExportLibrary(InteropLibrary.class) - static final class PyCFunctionVarargsWrapper extends PyCFunctionWrapper { + public 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); - } + @CApiUpcallTarget + @SuppressWarnings({"try", "unused"}) + public long executeVarargs(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, + Object receiver = NativeToPythonInternalNode.executeUncached(arg0, false); + Object starArgs = NativeToPythonInternalNode.executeUncached(arg1, false); + Object[] starArgsArray = ExecutePositionalStarargsNode.executeUncached(starArgs); + Object[] pArgs = CreateArgumentsNode.executeUncached(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 result = SimpleIndirectInvokeNode.executeUncached(callTarget, pArgs); + return PythonToNativeInternalNode.executeNewRefUncached(result); } catch (Throwable t) { throw checkThrowableBeforeNative(t, 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); } } - @Override - protected String getSignature() { - return "(POINTER,POINTER):POINTER"; - } - @Override protected String getFlagsRepr() { return "METH_VARARGS"; } } - @ExportLibrary(InteropLibrary.class) - static final class PyCFunctionKeywordsWrapper extends PyCFunctionWrapper { + public 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); - } + @CApiUpcallTarget + @SuppressWarnings({"try", "unused"}) + public long executeKeywords(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, + 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(callTargetName, starArgsArray, kwArgsArray, signature, receiver, null, defaults, PKeyword.EMPTY_KEYWORDS, false); - Object result = invokeNode.execute(null, inliningTarget, callTarget, pArgs); - return toNativeNode.execute(result); + Object result = SimpleIndirectInvokeNode.executeUncached(callTarget, pArgs); + return PythonToNativeInternalNode.executeNewRefUncached(result); } catch (Throwable t) { throw checkThrowableBeforeNative(t, 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); } } - @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..269cff21d8 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.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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,49 @@ 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(); PythonManagedClass date = (PythonManagedClass) getAttr.execute(null, datetimeModule, T_DATE); SetBasicSizeNode.executeUncached(date, CStructs.PyDateTime_Date.size()); - Object dateType = toNativeNode.execute(date); + long dateType = PythonToNativeInternalNode.executeNewRefUncached(date); PythonManagedClass dt = (PythonManagedClass) getAttr.execute(null, datetimeModule, T_DATETIME); SetBasicSizeNode.executeUncached(dt, CStructs.PyDateTime_DateTime.size()); - Object datetimeType = toNativeNode.execute(dt); + long datetimeType = PythonToNativeInternalNode.executeNewRefUncached(dt); PythonManagedClass time = (PythonManagedClass) getAttr.execute(null, datetimeModule, T_TIME); SetBasicSizeNode.executeUncached(time, CStructs.PyDateTime_Time.size()); - Object timeType = toNativeNode.execute(time); + long timeType = PythonToNativeInternalNode.executeNewRefUncached(time); PythonManagedClass delta = (PythonManagedClass) getAttr.execute(null, datetimeModule, T_TIMEDELTA); SetBasicSizeNode.executeUncached(delta, CStructs.PyDateTime_Delta.size()); - Object deltaType = toNativeNode.execute(delta); + long deltaType = PythonToNativeInternalNode.executeNewRefUncached(delta); - Object tzInfoType = toNativeNode.execute(getAttr.execute(null, datetimeModule, T_TZINFO)); + long tzInfoType = PythonToNativeInternalNode.executeNewRefUncached(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 = PythonToNativeInternalNode.executeNewRefUncached(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..6fcad6cfb3 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.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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, PythonToNativeInternalNode.executeNewRefUncached(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, PythonToNativeInternalNode.executeNewRefUncached(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..b73045de85 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TpSlotWrapper.java @@ -0,0 +1,1022 @@ +/* + * 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.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT32; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT64; +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.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.annotations.CApiUpcallTarget; +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.PythonToNativeInternalNode; +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.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.PythonOptions; +import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.graal.python.runtime.nativeaccess.NativeSignature; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; + +public abstract class TpSlotWrapper { + + private static final NativeSignature SIGNATURE_P_P = NativeSignature.create(POINTER, POINTER); + private static final NativeSignature SIGNATURE_P_PP = NativeSignature.create(POINTER, POINTER, POINTER); + private static final NativeSignature SIGNATURE_P_PPP = NativeSignature.create(POINTER, POINTER, POINTER, POINTER); + private static final NativeSignature SIGNATURE_P_PPI = NativeSignature.create(POINTER, POINTER, POINTER, SINT32); + private static final NativeSignature SIGNATURE_P_PL = NativeSignature.create(POINTER, POINTER, SINT64); + private static final NativeSignature SIGNATURE_I_P = NativeSignature.create(SINT32, POINTER); + private static final NativeSignature SIGNATURE_I_PP = NativeSignature.create(SINT32, POINTER, POINTER); + private static final NativeSignature SIGNATURE_I_PPP = NativeSignature.create(SINT32, POINTER, POINTER, POINTER); + private static final NativeSignature SIGNATURE_I_PLP = NativeSignature.create(SINT32, POINTER, SINT64, POINTER); + private static final NativeSignature SIGNATURE_L_P = NativeSignature.create(SINT64, 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 { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + HANDLE_GET_ATTR = lookup.findVirtual(GetAttrWrapper.class, "executeGetAttr", + MethodType.methodType(long.class, long.class, long.class)); + HANDLE_BINARY_SLOT_FUNC = lookup.findVirtual(BinarySlotFuncWrapper.class, "executeBinarySlot", + MethodType.methodType(long.class, long.class, long.class)); + HANDLE_BINARY_OP_SLOT_FUNC = lookup.findVirtual(BinaryOpSlotFuncWrapper.class, "executeBinaryOpSlot", + MethodType.methodType(long.class, long.class, long.class)); + HANDLE_UNARY_FUNC = lookup.findVirtual(UnaryFuncWrapper.class, "executeUnary", + MethodType.methodType(long.class, long.class)); + HANDLE_ITER_NEXT = lookup.findVirtual(IterNextWrapper.class, "executeIterNext", + MethodType.methodType(long.class, long.class)); + HANDLE_INQUIRY = lookup.findVirtual(InquiryWrapper.class, "executeInquiry", + MethodType.methodType(int.class, long.class)); + HANDLE_SQ_CONTAINS = lookup.findVirtual(SqContainsWrapper.class, "executeSqContains", + MethodType.methodType(int.class, long.class, long.class)); + HANDLE_OBJ_OBJ_ARG = lookup.findVirtual(ObjobjargWrapper.class, "executeObjobjarg", + MethodType.methodType(int.class, long.class, long.class, long.class)); + HANDLE_SET_ATTR = lookup.findVirtual(SetAttrWrapper.class, "executeSetAttr", + MethodType.methodType(int.class, long.class, long.class, long.class)); + HANDLE_DESCR_SET_FUNCTION = lookup.findVirtual(DescrSetFunctionWrapper.class, "executeDescrSetFunction", + MethodType.methodType(int.class, long.class, long.class, long.class)); + HANDLE_INIT = lookup.findVirtual(InitWrapper.class, "executeInit", + MethodType.methodType(int.class, long.class, long.class, long.class)); + HANDLE_NEW = lookup.findVirtual(NewWrapper.class, "executeNew", + MethodType.methodType(long.class, long.class, long.class, long.class)); + HANDLE_CALL = lookup.findVirtual(CallWrapper.class, "executeCall", + MethodType.methodType(long.class, long.class, long.class, long.class)); + HANDLE_NB_POWER = lookup.findVirtual(NbPowerWrapper.class, "executeNbPower", + MethodType.methodType(long.class, long.class, long.class, long.class)); + HANDLE_NB_IN_PLACE_POWER = lookup.findVirtual(NbInPlacePowerWrapper.class, "executeNbInPlacePower", + MethodType.methodType(long.class, long.class, long.class, long.class)); + HANDLE_RICHCMP_FUNCTION = lookup.findVirtual(RichcmpFunctionWrapper.class, "executeRichcmpFunction", + MethodType.methodType(long.class, long.class, long.class, int.class)); + HANDLE_SSIZEARGFUNC_SLOT = lookup.findVirtual(SsizeargfuncSlotWrapper.class, "executeSsizeargfuncSlot", + MethodType.methodType(long.class, long.class, long.class)); + HANDLE_SSIZEOBJARGPROC = lookup.findVirtual(SsizeobjargprocWrapper.class, "executeSsizeobjargproc", + MethodType.methodType(int.class, long.class, long.class, long.class)); + HANDLE_LENFUNC = lookup.findVirtual(LenfuncWrapper.class, "executeLenfunc", + MethodType.methodType(long.class, long.class)); + HANDLE_HASHFUNC = lookup.findVirtual(HashfuncWrapper.class, "executeHashfunc", + MethodType.methodType(long.class, long.class)); + HANDLE_DESCR_GET_FUNCTION = lookup.findVirtual(DescrGetFunctionWrapper.class, "executeDescrGetFunction", + MethodType.methodType(long.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 NativeSignature upcallSignature; + private final MethodHandle boundMethodHandle; + private final long nativePointer; + + @SuppressWarnings("this-escape") + TpSlotWrapper(TpSlotManaged slot, NativeSignature upcallSignature, MethodHandle methodHandle) { + this.timing = CApiTiming.create(false, slot); + this.slot = slot; + this.upcallSignature = upcallSignature; + this.boundMethodHandle = methodHandle.bindTo(this); + + if (canShareNativePointers()) { + nativePointer = registerClosure(); + } else { + nativePointer = NULLPTR; + } + } + + private static boolean canShareNativePointers() { + return PythonLanguage.get(null).isSingleContext() && !PythonContext.get(null).getOption(PythonOptions.IsolateNativeModules); + } + + private long registerClosure() { + CApiContext cApiContext = PythonContext.get(null).getCApiContext(); + return cApiContext.registerClosure(getClass().getSimpleName(), upcallSignature, boundMethodHandle, this, slot); + } + + public final TpSlotManaged getSlot() { + return slot; + } + + @TruffleBoundary + public final long getPointer() { + if (nativePointer != NULLPTR) { + assert canShareNativePointers(); + return nativePointer; + } + long pointer = PythonContext.get(null).getCApiContext().getClosurePointer(this); + return pointer == -1 ? registerClosure() : pointer; + } + + 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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeGetAttr(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(getSlot(), jArg0, jArg1); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "GetAttrWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeBinarySlot(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(getSlot(), jArg0, jArg1); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "BinarySlotFuncWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeBinaryOpSlot(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 = binaryOp.getSlotValue(GetTpSlotsNode.executeUncached(otherType)); + boolean sameTypes = IsSameTypeNode.executeUncached(receiverType, otherType); + Object result = CallSlotBinaryOpNode.executeUncached(getSlot(), receiver, receiverType, other, otherSlot, otherType, sameTypes, binaryOp); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "BinaryOpSlotFuncWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeUnary(long arg0) { + try (var gil = GilNode.uncachedAcquire()) { + CApiTiming.enter(); + try { + Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false); + Object result = CallSlotUnaryNode.executeUncached(getSlot(), jArg0); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "UnaryFuncWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeIterNext(long arg0) { + try (var gil = GilNode.uncachedAcquire()) { + CApiTiming.enter(); + try { + Object result; + try { + Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false); + result = CallSlotTpIterNextNode.executeUncached(getSlot(), jArg0); + } catch (IteratorExhausted e) { + return NULLPTR; + } + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "IterNextWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private int executeInquiry(long arg0) { + try (var gil = GilNode.uncachedAcquire()) { + CApiTiming.enter(); + try { + Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false); + return CallSlotNbBoolNode.executeUncached(getSlot(), jArg0) ? 1 : 0; + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "InquiryWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private int executeSqContains(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(getSlot(), jArg0, jArg1) ? 1 : 0; + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "SqContainsWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private int executeObjobjarg(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(getSlot(), jArg0, jArg1, jArg2); + return 0; + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "ObjobjargWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private int executeSetAttr(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(getSlot(), jArg0, jArg1, jArg2); + return 0; + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "SetAttrWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private int executeDescrSetFunction(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(getSlot(), jArg0, jArg1, jArg2); + return 0; + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "DescrSetFunctionWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private int executeInit(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(getSlot(), receiver, starArgsArray, kwArgsArray); + return 0; + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "InitWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeNew(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(getSlot(), receiver, pArgs, kwArgsArray); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "NewWrapper", 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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeCall(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(getSlot(), receiver, starArgsArray, kwArgsArray); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "CallWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeNbPower(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(getSlot(), v, vType, w, wSlots.nb_power(), wType, z, sameTypes); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "NbPowerWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeNbInPlacePower(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(getSlot(), v, w, z); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "NbInPlacePowerWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeRichcmpFunction(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(getSlot(), jArg0, jArg1, op); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "RichcmpFunctionWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeSsizeargfuncSlot(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(getSlot(), jArg0, index); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "SsizeargfuncWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private int executeSsizeobjargproc(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(getSlot(), jArg0, key, jArg2); + return 0; + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "SsizeobjargprocWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeLenfunc(long arg0) { + try (var gil = GilNode.uncachedAcquire()) { + CApiTiming.enter(); + try { + Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false); + return CallSlotLenNode.executeUncached(getSlot(), jArg0); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "LenfuncWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeHashfunc(long arg0) { + try (var gil = GilNode.uncachedAcquire()) { + CApiTiming.enter(); + try { + Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false); + return CallSlotHashFunNode.executeUncached(getSlot(), jArg0); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "HashfuncWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return -1; + } finally { + CApiTiming.exit(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); + } + + @CApiUpcallTarget + @SuppressWarnings("try") + private long executeDescrGetFunction(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(getSlot(), receiver, obj, cls); + return PythonToNativeInternalNode.executeNewRefUncached(result); + } catch (Throwable t) { + throw checkThrowableBeforeNative(t, "DescrGetFunctionWrapper", getSlot()); + } + } catch (PException e) { + TransformExceptionToNativeNode.executeUncached(e.getEscapedException()); + return NULLPTR; + } finally { + CApiTiming.exit(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..88c8f410d3 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 @@ -40,60 +40,55 @@ */ package com.oracle.graal.python.builtins.objects.cext.capi.transitions; -import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.FromLongNode; +import com.oracle.graal.python.annotations.NativeSimpleType; 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.util.Supplier; enum ArgBehavior { PyObject( - "POINTER", - "J", - "jlong", - "long", + NativeSimpleType.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.POINTER, ToNativeBorrowedNode::new, NativeToPythonNode::create, NativeToPythonNode.getUncached(), null, null, null), + PyObjectAsTruffleString(NativeSimpleType.POINTER, null, ToPythonStringNode::create, ToPythonStringNode.getUncached(), null, null, null), + PyTypeObject( + NativeSimpleType.POINTER, + PythonToNativeNode::create, + NativeToPythonClassNode::create, + NativeToPythonClassNode.getUncached(), + PythonToNativeNewRefNode::create, + NativeToPythonTransferNode::create, + NativeToPythonTransferNode.getUncached()), + Pointer(NativeSimpleType.POINTER), + TruffleStringPointer(NativeSimpleType.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), + 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 +96,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 +107,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 +122,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 +142,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*"), @@ -191,9 +188,11 @@ public enum ArgDescriptor { INT_LIST("int*"), INT8_T_PTR(ArgBehavior.Pointer, "int8_t*"), INT64_T(ArgBehavior.Int64, "int64_t"), + INTPTR_T_PTR(ArgBehavior.Pointer, "intptr_t*"), LONG_LONG(ArgBehavior.Int64, "long long"), LONG_PTR("long*"), DIGIT_PTR(ArgBehavior.Pointer, "digit*"), + INT64_T_PTR(ArgBehavior.Pointer, "int64_t*"), PyASCIIObject(ArgBehavior.PyObject, "PyASCIIObject*"), PY_AUDITHOOKFUNCTION("Py_AuditHookFunction"), Py_buffer("Py_buffer"), @@ -205,7 +204,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 +215,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 +229,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 +263,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 +282,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 +355,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 +405,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 +424,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 6fac3e14ce..4dd463333d 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 @@ -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,9 +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.readLongField; +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.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; @@ -52,61 +69,68 @@ import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; -import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.annotations.CApiConstant; 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.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.str.PString; +import com.oracle.graal.python.builtins.objects.str.StringNodes.StringMaterializeNode; 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.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.nativeaccess.NativeMemory; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; @@ -127,9 +151,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.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; @@ -149,12 +170,49 @@ public abstract class CApiTransitions { private static final TruffleLogger LOGGER = CApiContext.getLogger(CApiTransitions.class); + @CApiConstant // + public static final int GRAALPY_UNICODE_INTERN_STATE_UNDETERMINED = 0; + @CApiConstant // + public static final int GRAALPY_UNICODE_INTERN_STATE_INTERNED = 1; + @CApiConstant // + public static final int GRAALPY_UNICODE_INTERN_STATE_NOT_INTERNED = 2; + @CApiConstant // + private static final int GRAALPY_UNICODE_KIND_MASK = 0x7; + @CApiConstant // + private static final long GRAALPY_UNICODE_IS_ASCII_FLAG = 1L << 3; + @CApiConstant // + private static final int GRAALPY_UNICODE_INTERN_STATE_SHIFT = 4; + @CApiConstant // + private static final long GRAALPY_UNICODE_INTERN_STATE_MASK = 0x3L << GRAALPY_UNICODE_INTERN_STATE_SHIFT; + + enum PollingState { + /** startup barrier not finished yet, polling must not run */ + RQ_UNINITIALIZED, + + /** normal steady state, polling allowed */ + RQ_READY, + + /** one thread is currently polling */ + RQ_POLLING, + + /** temporarily disabled by GraalPyPrivate_DisableReferneceQueuePolling */ + RQ_DISABLED_TEMP, + + /** shutdown/finalization, end state */ + RQ_DISABLED_PERMANENT + } + 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. */ @@ -162,20 +220,20 @@ 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<>(); public final HashMap> nativeLookup = new HashMap<>(); 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<>(); @@ -183,7 +241,7 @@ public HandleContext(boolean useShadowTable) { public final ReferenceQueue referenceQueue = new ReferenceQueue<>(); - volatile boolean referenceQueuePollActive = false; + volatile PollingState referenceQueuePollingState = RQ_UNINITIALIZED; @TruffleBoundary public static T putShadowTable(HashMap table, long pointer, T ref) { @@ -195,6 +253,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); @@ -202,7 +270,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 { @@ -216,12 +284,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; /** @@ -235,17 +303,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() { @@ -301,7 +365,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); } } @@ -312,12 +376,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; @@ -345,7 +407,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) { @@ -358,11 +420,11 @@ public NativeStorageReference(HandleContext handleContext, NativeSequenceStorage } } - public Object getPtr() { + public long getPtr() { return ptr; } - public void setPtr(Object ptr) { + public void setPtr(long ptr) { this.ptr = ptr; } @@ -377,13 +439,7 @@ public void setSize(int size) { @Override @TruffleBoundary public String toString() { - Object ptrStr; - try { - ptrStr = Long.toHexString(InteropLibrary.getUncached().asPointer(ptr)); - } catch (UnsupportedMessageException e) { - ptrStr = ptr; - } - return String.format("NativeStorageReference<0x%s, %d>", ptrStr, size); + return String.format("NativeStorageReference<0x%x, %d>", ptr, size); } } @@ -396,6 +452,68 @@ public static void registerNativeSequenceStorage(NativeSequenceStorage storage) handleContext.nativeStorageReferences.add(ref); } + public static long initializeGraalPyUnicodeObject(long rawPointer, long elements, long byteLength, int charSize, boolean isAscii, int interned) { + assert charSize == 1 || charSize == 2 || charSize == 4; + assert byteLength == elements * charSize; + assert interned == GRAALPY_UNICODE_INTERN_STATE_UNDETERMINED || interned == GRAALPY_UNICODE_INTERN_STATE_INTERNED || interned == GRAALPY_UNICODE_INTERN_STATE_NOT_INTERNED; + long data = rawPointer + CStructs.GraalPyUnicodeObject.size(); + writeLongField(rawPointer, CFields.GraalPyUnicodeObject__length, elements); + writeLongField(rawPointer, CFields.GraalPyUnicodeObject__byte_length, byteLength); + writeLongField(rawPointer, CFields.GraalPyUnicodeObject__hash, -1); + writeLongField(rawPointer, CFields.GraalPyUnicodeObject__state, createGraalPyUnicodeObjectState(charSize, isAscii, interned)); + writePtrField(rawPointer, CFields.GraalPyUnicodeObject__data, data); + NativeMemory.memset(data + byteLength, (byte) 0, charSize); + return data; + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_CreateState. + private static long createGraalPyUnicodeObjectState(int charSize, boolean isAscii, int interned) { + assert (charSize & ~GRAALPY_UNICODE_KIND_MASK) == 0; + return charSize | encodeGraalPyUnicodeObjectAscii(isAscii) | encodeGraalPyUnicodeObjectInterned(interned); + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_EncodeAscii. + private static long encodeGraalPyUnicodeObjectAscii(boolean isAscii) { + return isAscii ? GRAALPY_UNICODE_IS_ASCII_FLAG : 0; + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_EncodeInterned. + private static long encodeGraalPyUnicodeObjectInterned(int interned) { + return (long) interned << GRAALPY_UNICODE_INTERN_STATE_SHIFT; + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_GetInternedFromState. + private static int getGraalPyUnicodeObjectInternedFromState(long state) { + return (int) ((state & GRAALPY_UNICODE_INTERN_STATE_MASK) >> GRAALPY_UNICODE_INTERN_STATE_SHIFT); + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_IsAsciiFromState. + private static boolean isGraalPyUnicodeObjectAsciiFromState(long state) { + return (state & GRAALPY_UNICODE_IS_ASCII_FLAG) != 0; + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_GetKindFromState. + private static int getGraalPyUnicodeObjectKindFromState(long state) { + return (int) (state & GRAALPY_UNICODE_KIND_MASK); + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_UpdateInterned. + private static long updateGraalPyUnicodeObjectInterned(long state, int interned) { + return (state & ~GRAALPY_UNICODE_INTERN_STATE_MASK) | encodeGraalPyUnicodeObjectInterned(interned); + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_GetKind. + public static int getGraalPyUnicodeObjectKind(long rawPointer) { + return getGraalPyUnicodeObjectKindFromState(readLongField(rawPointer, CFields.GraalPyUnicodeObject__state)); + } + + // Keep in sync with unicodeobject.c:GraalPyUnicodeObject_SetInterned. + public static void setGraalPyUnicodeObjectInterned(long rawPointer, int interned) { + assert interned == GRAALPY_UNICODE_INTERN_STATE_UNDETERMINED || interned == GRAALPY_UNICODE_INTERN_STATE_INTERNED || interned == GRAALPY_UNICODE_INTERN_STATE_NOT_INTERNED; + long state = readLongField(rawPointer, CFields.GraalPyUnicodeObject__state); + writeLongField(rawPointer, CFields.GraalPyUnicodeObject__state, updateGraalPyUnicodeObjectInterned(state, interned)); + } + public static final class PyCapsuleReference extends IdReference { private final PyCapsule.CapsuleData data; @@ -427,31 +545,50 @@ 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.referenceQueuePollActive) { - try (GilNode.UncachedAcquire ignored = GilNode.uncachedAcquire()) { - ReferenceQueue queue = handleContext.referenceQueue; - int count = 0; - long start = 0; - ArrayList referencesToBeFreed = handleContext.referencesToBeFreed; + if (handleContext.referenceQueuePollingState != RQ_READY) { + return manuallyCollected; + } + /* + * Polling the reference queue may deallocate native GC objects and therefore re-enter + * native code paths that use '_PyThreadState_GET()' to obtain the current thread's GC + * state. So, we may only poll once the current thread has installed its native + * 'tstate_current' pointer. + */ + if (!context.getThreadState(context.getLanguage()).isNativeThreadStateInitialized()) { + return manuallyCollected; + } + try (GilNode.UncachedAcquire ignored = GilNode.uncachedAcquire()) { + if (handleContext.referenceQueuePollingState != RQ_READY) { + return manuallyCollected; + } + if (!context.getThreadState(context.getLanguage()).isNativeThreadStateInitialized()) { + return manuallyCollected; + } + ReferenceQueue queue = handleContext.referenceQueue; + int count = 0; + long start = 0; + boolean polling = false; + ArrayList referencesToBeFreed = handleContext.referencesToBeFreed; + try { while (true) { Object entry = queue.poll(); if (entry == null) { if (count > 0) { - assert handleContext.referenceQueuePollActive; + assert handleContext.referenceQueuePollingState == RQ_POLLING || handleContext.referenceQueuePollingState == RQ_DISABLED_PERMANENT; releaseNativeObjects(context, referencesToBeFreed); - handleContext.referenceQueuePollActive = false; LOGGER.fine("collected " + count + " references from native reference queue in " + ((System.nanoTime() - start) / 1000000) + "ms"); } return manuallyCollected; } if (count == 0) { - assert !handleContext.referenceQueuePollActive; - handleContext.referenceQueuePollActive = true; + assert handleContext.referenceQueuePollingState == RQ_READY; + handleContext.referenceQueuePollingState = RQ_POLLING; + polling = true; start = System.nanoTime(); } else { - assert handleContext.referenceQueuePollActive; + assert handleContext.referenceQueuePollingState == RQ_POLLING; } count++; LOGGER.fine(() -> PythonUtils.formatJString("releasing %s, no remaining managed references", entry)); @@ -483,7 +620,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; /* @@ -515,7 +652,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)); @@ -529,16 +666,18 @@ public static int pollReferenceQueue() { processPyCapsuleReference(reference); } } + } finally { + if (polling && handleContext.referenceQueuePollingState == RQ_POLLING) { + handleContext.referenceQueuePollingState = RQ_READY; + } } } - return manuallyCollected; } /** - * 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())); @@ -559,18 +698,34 @@ 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); + assert reference.ptr != NULLPTR; freeNativeStorage(reference); } 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 = PythonToNativeInternalNode.executeUncached(capsule, false); + 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); + } } } @@ -587,6 +742,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. @@ -595,12 +752,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)); + } + try { + ExternalFunctionInvoker.invokeBULK_DEALLOC( + CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_BULK_DEALLOC).getAddress(), pointer, size); + } catch (Throwable t) { + throw CompilerDirectives.shouldNotReachHere(t); } - PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_BULK_DEALLOC, pointer, size); - FreeNode.freeLong(pointer); + free(pointer); referencesToBeFreed.clear(); } finally { CExtCommonNodes.ReadAndClearNativeException.executeUncached(threadState); @@ -611,53 +773,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()) { @@ -684,55 +848,77 @@ 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) { - if (!handleContext.referenceQueuePollActive) { - handleContext.referenceQueuePollActive = true; + if (handleContext.referenceQueuePollingState == RQ_READY) { + handleContext.referenceQueuePollingState = RQ_DISABLED_TEMP; return false; } return true; } public static void enableReferenceQueuePolling(HandleContext handleContext) { - handleContext.referenceQueuePollActive = false; + if (handleContext.referenceQueuePollingState == RQ_DISABLED_TEMP) { + handleContext.referenceQueuePollingState = RQ_READY; + } + } + + public static void initializeReferenceQueuePolling(HandleContext handleContext) { + assert handleContext.referenceQueuePollingState == RQ_UNINITIALIZED : handleContext.referenceQueuePollingState; + handleContext.referenceQueuePollingState = RQ_READY; + } + + public static void disableReferenceQueuePollingPermanently(HandleContext handleContext) { + handleContext.referenceQueuePollingState = RQ_DISABLED_PERMANENT; } 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); } /** @@ -744,11 +930,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. } } @@ -771,7 +974,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); } /** @@ -780,7 +983,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) { @@ -799,24 +1002,40 @@ 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(); + Long[] list = context.nativeWeakRef.keySet().toArray(new Long[0]); context.nativeWeakRef.clear(); long[] ptrArray = new long[list.length]; - for (Object ptr : list) { - if (context.nativeLookup.containsKey(ptr)) { - ptrArray[++idx] = (Long) ptr; + for (Long ptr : list) { + if (ptr != NULLPTR) { + IdReference ref = nativeLookupGet(context, ptr); + Object object = ref != null ? ref.get() : null; + long refCount = ref != null ? readNativeRefCount(ptr) : 0; + /* + * Only deallocate weakref referents whose managed wrapper has already been + * collected and that have no additional native owners. Forcing deallocation while + * native owners remain can leave dangling references for later shutdown DECREFs. + */ + if (object == null && refCount == MANAGED_REFCNT) { + nativeLookupRemove(context, ptr); + ptrArray[++idx] = ptr; + } } } 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(); } } @@ -859,13 +1078,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 @@ -873,14 +1096,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(); } @@ -889,8 +1168,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 @@ -919,13 +1197,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(); } @@ -933,13 +1212,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; @@ -1005,160 +1304,217 @@ 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 final long execute(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper) { - return execute(inliningTarget, wrapper, false); + public static long executeUncached(PythonAbstractObject object, long initialRefCount) { + return FirstToNativeNodeGen.getUncached().execute(null, object, initialRefCount); } - 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, 0); } - @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, + @Exclusive @Cached InlinedConditionProfile isStringObjectProfile, + @Cached GetPythonObjectClassNode getClassNode, @Cached(inline = false) GetTypeFlagsNode getTypeFlagsNode, + @Cached TruffleString.GetCodeRangeNode getCodeRangeNode, + @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Cached CStructAccess.WriteTruffleStringNode writeTruffleStringNode, @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)) { + TruffleString unicodeString = null; + TruffleString.Encoding unicodeEncoding = null; + int unicodeCharSize = 0; + boolean unicodeIsAscii = false; + long unicodeByteLength = 0; + long extraSize = 0; + 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 if (isStringObjectProfile.profile(inliningTarget, pythonObject instanceof PString)) { + ctype = CStructs.GraalPyUnicodeObject; + PString stringObject = (PString) pythonObject; + if (!stringObject.isMaterialized()) { + throw CompilerDirectives.shouldNotReachHere("unmaterialized PString should already have a native unicode stub"); + } + unicodeString = stringObject.getMaterialized(); + TruffleString.CodeRange range = getCodeRangeNode.execute(unicodeString, PythonUtils.TS_ENCODING); + if (range == TruffleString.CodeRange.ASCII) { + unicodeIsAscii = true; + unicodeCharSize = 1; + unicodeEncoding = TruffleString.Encoding.US_ASCII; + } else if (range.isSubsetOf(TruffleString.CodeRange.LATIN_1)) { + unicodeCharSize = 1; + unicodeEncoding = TruffleString.Encoding.ISO_8859_1; + } else if (range.isSubsetOf(TruffleString.CodeRange.BMP)) { + unicodeCharSize = 2; + unicodeEncoding = TruffleString.Encoding.UTF_16; + } else { + unicodeCharSize = 4; + unicodeEncoding = TruffleString.Encoding.UTF_32; + } + unicodeString = switchEncodingNode.execute(unicodeString, unicodeEncoding); + unicodeByteLength = unicodeString.byteLength(unicodeEncoding); + extraSize = unicodeByteLength + unicodeCharSize; } 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, extraSize); // 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()); + } else if (ctype == CStructs.GraalPyUnicodeObject) { + assert pythonObject instanceof PString; + long realPointer = HandlePointerConverter.pointerToStub(taggedPointer); + long data = initializeGraalPyUnicodeObject(realPointer, unicodeByteLength / unicodeCharSize, unicodeByteLength, unicodeCharSize, unicodeIsAscii, + GRAALPY_UNICODE_INTERN_STATE_UNDETERMINED); + writeTruffleStringNode.write(data, unicodeString, unicodeEncoding); } return taggedPointer; } + + public static long getInitialRefcnt(boolean newRef, boolean immortal) { + if (immortal) { + return IMMORTAL_REFCNT; + } + return MANAGED_REFCNT + (newRef ? 1 : 0); + } } @GenerateUncached @GenerateInline @GenerateCached(false) - abstract static class AllocateNativeObjectStubNode extends Node { + public abstract static class AllocateNativeObjectStubNode extends Node { + + @TruffleBoundary + public static long executeUncached(PythonAbstractObject object, Object type, CStructs ctype, long initialRefCount, boolean gc, long extraSize) { + return AllocateNativeObjectStubNodeGen.getUncached().execute(null, object, type, ctype, initialRefCount, gc, extraSize); + } - abstract long execute(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper, Object type, CStructs ctype, boolean immortal, boolean gc); + public abstract long execute(Node inliningTarget, PythonAbstractObject object, Object type, CStructs ctype, long initialRefCount, boolean gc, long extraSize); @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, long extraSize, @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 allocationSize = ctype.size() + presize + extraSize; + long stubPointer = NativeMemory.malloc(allocationSize); + NativeMemory.memset(stubPointer, (byte) 0, 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') @@ -1168,11 +1524,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(); @@ -1182,9 +1539,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 @@ -1206,7 +1583,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 @@ -1217,23 +1594,57 @@ 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 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}. - *

    - * This is used in LLVM managed mode where we will not have real native pointers (i.e. addresses - * pointing into off-heap memory) but managed pointers to objects emulating the native memory. - * We still need to be able to resolve those managed pointers to our objects. - *

    + * 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 void createManagedReference(Object delegate, Object pointer) { + public static int createPythonNativeClassReference(PythonNativeClass clazz) { + CompilerAsserts.neverPartOfCompilation(); assert PythonContext.get(null).ownsGil(); - getContext().managedNativeLookup.put(pointer, new WeakReference<>(delegate)); + + 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; + } } // logging @@ -1297,83 +1708,27 @@ 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 { + @TruffleBoundary + public static Object executeUncached(long pointer) { + return CApiTransitionsFactory.CharPtrToPythonNodeGen.getUncached().execute(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 @@ -1387,29 +1742,76 @@ 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 { + + public final long execute(Node inliningTarget, Object object) { + return execute(inliningTarget, object, false); + } + + public final long executeNewRef(Node inliningTarget, Object object) { + return execute(inliningTarget, object, true); + } @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; + @TruffleBoundary + public static long executeNewRefUncached(Object obj) { + Object promoted = EnsurePythonObjectNode.executeUncached(PythonContext.get(null), obj, false); + return executeUncached(promoted, true); } + abstract long execute(Node inliningTarget, Object object, boolean needsTransfer); + @Specialization - static Object doNative(PythonAbstractNativeObject obj, - @Bind Node inliningTarget, - @Bind("needsTransfer()") boolean needsTransfer, - @CachedLibrary(limit = "2") InteropLibrary lib, - @Cached InlinedBranchProfile inlinedBranchProfile, + 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 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 @@ -1421,12 +1823,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()); } } } @@ -1434,65 +1836,187 @@ 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 = 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 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 wrapper != null; - return wrapper; + 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) { + assert !CApiContext.isSpecialSingleton(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; + } + + // 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 { + + @Specialization + static long doGeneric(Object obj, + @Bind Node inliningTarget, + @Cached PythonToNativeInternalNode internalNode) { + return internalNode.execute(inliningTarget, obj); } @NeverDefault @@ -1534,22 +2058,19 @@ 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(); - } - - @TruffleBoundary - public static Object executeUncached(Object obj) { - return PythonToNativeNewRefNodeGen.getUncached().execute(obj); - } - - @Override - protected final boolean needsTransfer() { - return true; + 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.executeNewRef(inliningTarget, promoted); } @NeverDefault @@ -1563,78 +2084,62 @@ public static PythonToNativeNewRefNode getUncached() { } @GenerateUncached - @GenerateInline(false) - @ImportStatic(CApiGuards.class) - public abstract static class NativeToPythonNode extends CExtToJavaNode { + @GenerateInline + @GenerateCached(false) + public abstract static class NativeToPythonInternalNode extends Node { - public abstract Object execute(PythonNativeWrapper object); + public final Object execute(Node inliningTarget, long value) { + return execute(inliningTarget, value, false, false); + } - @TruffleBoundary - public static Object executeUncached(Object obj) { - return NativeToPythonNodeGen.getUncached().execute(obj); + public final Object executeTransfer(Node inliningTarget, long object) { + return execute(inliningTarget, object, true, false); } - protected boolean needsTransfer() { - return false; + public final Object executeTransferAndRelease(Node inliningTarget, long object) { + return execute(inliningTarget, object, true, true); } - @Specialization - static Object doWrapper(PythonNativeWrapper value, - @Bind Node inliningTarget, - @Exclusive @Cached InlinedExactClassProfile wrapperProfile, - @Exclusive @Cached UpdateStrongRefNode updateRefNode) { - return handleWrapper(inliningTarget, wrapperProfile, updateRefNode, false, value); + abstract Object execute(Node inliningTarget, long value, boolean needsTransfer, boolean release); + + @TruffleBoundary + public static Object executeUncached(long value, boolean needsTransfer) { + return NativeToPythonInternalNodeGen.getUncached().execute(null, value, needsTransfer, false); } - @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 @@ -1647,9 +2152,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; } @@ -1657,43 +2161,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 @@ -1704,37 +2210,32 @@ 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 { + + @TruffleBoundary + public static Object executeRawUncached(long pointer) { + return NativeToPythonNodeGen.getUncached().execute(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 executeUncached(long pointer) { + return executeRawUncached(pointer); + } + + @Specialization + static Object doLong(long value, + @Bind Node inliningTarget, + @Shared @Cached NativeToPythonInternalNode nativeToPythonInternalNode) { + return nativeToPythonInternalNode.execute(inliningTarget, value); } @NeverDefault @@ -1749,65 +2250,75 @@ 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(); + static Object doLong(long pointer, + @Bind Node inliningTarget, + @Shared @Cached NativeToPythonInternalNode nativeToPythonInternalNode) { + return nativeToPythonInternalNode.executeTransfer(inliningTarget, pointer); + } + + @NeverDefault + public static NativeToPythonTransferNode create() { + return NativeToPythonTransferNodeGen.create(); } + public static NativeToPythonTransferNode getUncached() { + return NativeToPythonTransferNodeGen.getUncached(); + } + } + + @GenerateUncached + @GenerateInline(false) + public abstract static class NativeToPythonReturnNode extends CExtToJavaNode { + @TruffleBoundary - public static Object executeUncached(Object obj) { - return NativeToPythonTransferNodeGen.getUncached().execute(obj); + public static Object executeUncached(long pointer) { + return NativeToPythonReturnNodeGen.getUncached().execute(pointer); } - @Override - protected final boolean needsTransfer() { - return true; + @Specialization + static Object doLong(long pointer, + @Bind Node inliningTarget, + @Shared @Cached NativeToPythonInternalNode nativeToPythonInternalNode) { + return nativeToPythonInternalNode.executeTransferAndRelease(inliningTarget, pointer); } @NeverDefault - public static NativeToPythonTransferNode create() { - return NativeToPythonTransferNodeGen.create(); + public static NativeToPythonReturnNode create() { + return NativeToPythonReturnNodeGen.create(); } - public static NativeToPythonTransferNode getUncached() { - return NativeToPythonTransferNodeGen.getUncached(); + public static NativeToPythonReturnNode getUncached() { + return NativeToPythonReturnNodeGen.getUncached(); } } @GenerateUncached @GenerateInline(false) - @ImportStatic(CApiGuards.class) public abstract static class NativePtrToPythonNode extends PNodeWithContext { public abstract Object execute(long object, boolean stealing); - @TruffleBoundary - public static Object executeUncached(long object, boolean stealing) { - return NativePtrToPythonNodeGen.getUncached().execute(object, 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; @@ -1819,39 +2330,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); } } @@ -1870,17 +2386,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(); @@ -1890,9 +2409,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. @@ -1900,31 +2425,147 @@ 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 || referent instanceof PythonObject; + 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(long obj) { + return NativeToPythonClassNodeGen.getUncached().execute(obj); + } + + @Specialization + static Object doLong(long value, + @Bind Node inliningTarget, + @Shared @Cached NativeToPythonClassInternalNode nativeToPythonInternalNode) { + return nativeToPythonInternalNode.execute(inliningTarget, value); + } + + @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) { @@ -1943,6 +2584,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; } @@ -1950,6 +2592,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; @@ -1984,23 +2627,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; @@ -2008,151 +2681,95 @@ private static Object createAbstractNativeObject(HandleContext handleContext, Ob @TruffleBoundary public static boolean isBackendPointerObject(Object obj) { - return obj != null && (obj.getClass().toString().contains("LLVMPointerImpl") || obj.getClass().toString().contains("NFIPointer") || obj.getClass().toString().contains("NativePointer")); + return obj != null && 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, + @Cached StringMaterializeNode stringMaterializeNode) { + 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, stringMaterializeNode); } } } /** - * 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); + } + + @TruffleBoundary + public static void executeUncached(HandleContext handleContext, long pointer, int handleTableIndex, long refCount) { + CApiTransitionsFactory.UpdateHandleTableReferenceNodeGen.getUncached().execute(null, handleContext, pointer, handleTableIndex, refCount); } /** @@ -2161,42 +2778,161 @@ 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()) { + 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; + + // note: parameter 'stringMaterializeNode' can be null because 'release' is false + makeDirectReferenceWeak(inliningTarget, handleContext, pythonObject, taggedPointer, handleTableIndex, + false, keepInGcList, isLoggable, + gcListRemoveNode, isGcProfile, getClassNode, getTypeFlagsNode, null); + } + } + + 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, + StringMaterializeNode stringMaterializeNode) { + 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) { + if (pythonObject instanceof PString stringObject && !stringObject.isMaterialized()) { /* - * 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. + * The native companion is about to be released, so materialization must not + * keep a view of GraalPyUnicodeObject.data. */ - if (!keepInGcList && ref.gc) { - gcListRemoveNode.executeOp(inliningTarget, ref.pointer); - } - ref.setStrongReference(null); + stringMaterializeNode.execute(inliningTarget, stringObject, true); + } + // 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..a98b71a7a7 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ToNativeTypeNode.java @@ -0,0 +1,391 @@ +/* + * 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.PythonToNativeInternalNode; +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; +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.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; + +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); + + 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, PythonToNativeInternalNode.executeUncached(promotedType, false)); + } + + 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); + + // TODO(fa): the allocated 'char *' will be free'd at context finalization. It should be + // free'd if the type is free'd. + long namePointer = ctx.stringToNativeUtf8Bytes(TypeNodes.GetTpNameNode.executeUncached(clazz), true); + + 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, PythonToNativeInternalNode.executeUncached(promotedBase, false)); + } + + // 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, PythonToNativeInternalNode.executeUncached(dict, false)); + + 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, PythonToNativeInternalNode.executeUncached(clazz.basesTuple, false)); + if (clazz.mroStore == null) { + clazz.mroStore = PFactory.createTuple(language, GetMroStorageNode.executeUncached(clazz)); + } + assert EnsurePythonObjectNode.doesNotNeedPromotion(clazz.mroStore); + writePtrField(mem, CFields.PyTypeObject__tp_mro, PythonToNativeInternalNode.executeUncached(clazz.mroStore, false)); + writePtrField(mem, CFields.PyTypeObject__tp_cache, NULLPTR); + PDict subclasses = GetSubclassesNode.executeUncached(clazz); + writePtrField(mem, CFields.PyTypeObject__tp_subclasses, PythonToNativeInternalNode.executeNewRefUncached(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, PythonToNativeInternalNode.executeNewRefUncached(clazz.getName())); + writePtrField(mem, CFields.PyHeapTypeObject__ht_qualname, PythonToNativeInternalNode.executeNewRefUncached(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 ? PythonToNativeInternalNode.executeNewRefUncached(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 4abb4d0f1e..731dc4219b 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 @@ -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 @@ -41,56 +41,54 @@ 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.builtins.objects.str.StringUtils.byteIndexToCodepointIndex; 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; import static com.oracle.graal.python.runtime.exception.PythonErrorType.SystemError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.UnicodeEncodeError; +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.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; 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.CExtNodes.EnsurePythonObjectNode; 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.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PConstructAndRaiseNode; 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; @@ -99,11 +97,10 @@ 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.nativeaccess.NativeFunctionPointer; 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 +113,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 { @@ -158,6 +147,9 @@ static Object doObj(Object o) { @GenerateInline(false) // footprint reduction 40 -> 22 @GenerateUncached public abstract static class EncodeNativeStringNode extends PNodeWithContext { + private static final TruffleString.CodePointSet SURROGATES = TruffleString.CodePointSet.fromRanges(new int[]{ + Character.MIN_SURROGATE, Character.MAX_SURROGATE, + }, TS_ENCODING); public abstract TruffleString execute(TruffleString.Encoding encoding, Object unicodeObject, TruffleString errors); @@ -169,9 +161,13 @@ static TruffleString doGeneric(TruffleString.Encoding encoding, Object unicodeOb @Cached TruffleString.IsValidNode isValidNode, @Cached TruffleString.GetCodeRangeNode getCodeRangeNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Cached TruffleString.ByteIndexOfCodePointSetNode byteIndexOfCodePointSetNode, + @Cached TruffleString.CodePointLengthNode codePointLengthNode, + @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode, @Cached InlinedConditionProfile strictProfile, @Cached InlinedConditionProfile ignoreProfile, @Cached InlinedConditionProfile replaceProfile, + @Cached PConstructAndRaiseNode constructAndRaiseNode, @Cached PRaiseNode raiseNode) { assert encoding == TruffleString.Encoding.US_ASCII || encoding == TruffleString.Encoding.ISO_8859_1 || @@ -195,7 +191,7 @@ static TruffleString doGeneric(TruffleString.Encoding encoding, Object unicodeOb if (!isValidNode.execute(str, TS_ENCODING)) { // any invalid string will trigger an exception when strict mode is used, so // we don't even need to try - throw raiseNode.raise(inliningTarget, UnicodeEncodeError, ErrorMessages.M); + throw raiseSurrogatesEncodeError(constructAndRaiseNode, str, byteIndexOfCodePointSetNode, codePointLengthNode, codePointAtIndexNode); } if (encoding == TruffleString.Encoding.ISO_8859_1 || encoding == TruffleString.Encoding.US_ASCII) { // if the target encoding is ASCII or LATIN-1, transcoding will still fail @@ -212,6 +208,38 @@ static TruffleString doGeneric(TruffleString.Encoding encoding, Object unicodeOb return switchEncodingNode.execute(str, encoding); } } + + private static PException raiseSurrogatesEncodeError(PConstructAndRaiseNode constructAndRaiseNode, TruffleString str, + TruffleString.ByteIndexOfCodePointSetNode byteIndexOfCodePointSetNode, + TruffleString.CodePointLengthNode codePointLengthNode, + TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { + int start = findFirstSurrogateIndex(str, byteIndexOfCodePointSetNode); + int end = findSurrogateRangeEnd(str, start, codePointLengthNode, codePointAtIndexNode); + return constructAndRaiseNode.raiseUnicodeEncodeError(null, "utf-8", str, start, end, + "surrogates not allowed"); + } + + private static int findFirstSurrogateIndex(TruffleString str, TruffleString.ByteIndexOfCodePointSetNode byteIndexOfCodePointSetNode) { + int byteIndex = byteIndexOfCodePointSetNode.execute(str, 0, str.byteLength(TS_ENCODING), SURROGATES); + return byteIndex < 0 ? 0 : byteIndexToCodepointIndex(byteIndex); + } + + private static int findSurrogateRangeEnd(TruffleString str, int start, TruffleString.CodePointLengthNode codePointLengthNode, + TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { + int length = codePointLengthNode.execute(str, TS_ENCODING); + int end = start + 1; + while (end < length) { + if (!isSurrogate(codePointAtIndexNode.execute(str, end))) { + break; + } + end++; + } + return end; + } + + private static boolean isSurrogate(int codePoint) { + return Character.MIN_SURROGATE <= codePoint && codePoint <= Character.MAX_SURROGATE; + } } public abstract static class Charsets { @@ -260,62 +288,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 +348,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 +373,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 +385,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 +404,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 +413,17 @@ 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 EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode 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); + Object promotedException = ensurePythonObjectNode.execute(PythonContext.get(inliningTarget), pythonException, false); + long currentException = pythonToNativeNode.executeNewRef(inliningTarget, promotedException); + 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 +437,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) { @@ -545,14 +484,13 @@ 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); + static Object getException(Node inliningTarget, PythonThreadState threadState, + @Cached CApiTransitions.NativeToPythonInternalNode nativeToPythonNode) { + long nativeThreadState = threadState.getNativePointer(); + if (nativeThreadState != PythonAbstractObject.UNINITIALIZED) { + assert nativeThreadState != PythonAbstractObject.NATIVE_POINTER_FREED; + Object exception = nativeToPythonNode.executeTransfer(inliningTarget, readPtrField(nativeThreadState, CFields.PyThreadState__current_exception)); + writePtrField(nativeThreadState, CFields.PyThreadState__current_exception, 0L); return exception; } return PNone.NO_VALUE; @@ -564,6 +502,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}). @@ -617,7 +560,7 @@ private static void checkFunctionResultSlowpath(Node inliningTarget, TruffleStri if (indicatesError) { if (errOccurred) { assert currentException != PNone.NO_VALUE; - throw PException.fromObject(currentException, inliningTarget, false); + throw PException.fromObjectFixUncachedLocation(currentException, inliningTarget, false); } else if (strict) { assert currentException == PNone.NO_VALUE; throw raiseNullButNoError(inliningTarget, name, nullButNoErrorMessage); @@ -646,32 +589,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 +740,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 +799,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 +857,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,55 +884,19 @@ 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); } } - /** - * This node converts a {@link String} object to a {@link TruffleString} or it converts a - * {@code NULL} pointer to {@link PNone#NONE}. This is a very special use case and certainly - * only good for reading a member of type - * {@link com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodes#T_STRING}. - */ - @GenerateInline(false) // footprint reduction 32 -> 13, inherits non-inlineable execute() - @GenerateUncached - public abstract static class StringAsPythonStringNode extends CExtToJavaNode { - - @Specialization - static TruffleString doJavaString(String value, - @Cached TruffleString.FromJavaStringNode fromJavaStringNode) { - // TODO review with GR-37896 - return fromJavaStringNode.execute(value, TS_ENCODING); - } - - @Specialization - static TruffleString doTruffleString(TruffleString value) { - return value; - } - - @SuppressWarnings("unused") - @Specialization(guards = "interopLib.isNull(value)", limit = "3") - static Object doGeneric(Object value, - @CachedLibrary("value") InteropLibrary interopLib) { - return PNone.NONE; - } - - @Specialization - static TruffleString doNative(Object value, - @Cached FromCharPointerNode fromPtr) { - return fromPtr.execute(value); - } - } - /** * This node converts a C Boolean value to Python Boolean. */ @GenerateInline(false) // footprint reduction 24 -> 5, inherits non-inlineable execute() @GenerateUncached - public abstract static class NativePrimitiveAsPythonBooleanNode extends CExtToJavaNode { + public abstract static class NativePrimitiveAsPythonBooleanNode extends Node { + + public abstract Object execute(Object value); @Specialization static Boolean doBoolean(Boolean b) { @@ -1061,112 +934,6 @@ static Object doGeneric(Object n, } } - /** - * This node converts a native primitive value to an appropriate Python char value (a - * single-char Python string). - */ - @GenerateInline(false) // footprint reduction 36 -> 17 - @GenerateUncached - public abstract static class NativePrimitiveAsPythonCharNode extends CExtToJavaNode { - - @Specialization - static TruffleString doByte(byte b, - @Shared("fromInt") @Cached TruffleString.FromIntArrayUTF32Node fromIntArrayNode, - @Shared("switchEnc") @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { - // fromIntArrayNode return utf32, thich is at this point the same as TS_ENCODING, - // but might change in the future - return switchEncodingNode.execute(fromIntArrayNode.execute(new int[]{b}), TS_ENCODING); - } - - @Specialization - static TruffleString doShort(short i, - @Shared("fromInt") @Cached TruffleString.FromIntArrayUTF32Node fromIntArrayNode, - @Shared("switchEnc") @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { - // fromIntArrayNode return utf32, thich is at this point the same as TS_ENCODING, - // but might change in the future - return switchEncodingNode.execute(fromIntArrayNode.execute(new int[]{i}, 0, 1), TS_ENCODING); - } - - @Specialization - static TruffleString doLong(long l, - @Cached TruffleString.FromLongNode fromLongNode) { - return fromLongNode.execute(l, TS_ENCODING, true); - } - - @Specialization(replaces = {"doByte", "doShort", "doLong"}, limit = "1") - static Object doGeneric(Object n, - @CachedLibrary("n") InteropLibrary lib, - @Shared("fromInt") @Cached TruffleString.FromIntArrayUTF32Node fromIntArrayNode, - @Shared("switchEnc") @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { - if (lib.fitsInShort(n)) { - try { - // fromIntArrayNode return utf32, thich is at this point the same as - // TS_ENCODING, - // but might change in the future - return switchEncodingNode.execute(fromIntArrayNode.execute(new int[]{lib.asShort(n)}, 0, 1), TS_ENCODING); - } catch (UnsupportedMessageException e) { - // fall through - } - } - throw CompilerDirectives.shouldNotReachHere(); - } - } - - @GenerateInline(false) // footprint reduction 20 -> 1, inherits non-inlineable execute() - @GenerateUncached - public abstract static class NativeUnsignedByteNode extends CExtToJavaNode { - - @Specialization - static int doUnsignedIntPositive(int n) { - return n & 0xff; - } - } - - @GenerateInline(false) // footprint reduction 20 -> 1, inherits non-inlineable execute() - @GenerateUncached - public abstract static class NativeUnsignedShortNode extends CExtToJavaNode { - - @Specialization - static int doUnsignedIntPositive(int n) { - return n & 0xffff; - } - } - - /** - * This node converts a native primitive value to an appropriate Python value considering the - * native value as unsigned. For example, a negative {@code int} value will be converted to a - * positive {@code long} value. - */ - @GenerateInline(false) // footprint reduction 24 -> 5, inherits non-inlineable execute() - - @GenerateUncached - public abstract static class NativeUnsignedPrimitiveAsPythonObjectNode extends CExtToJavaNode { - - @Specialization(guards = "n >= 0") - static int doUnsignedIntPositive(int n) { - return n; - } - - @Specialization(replaces = "doUnsignedIntPositive") - static long doUnsignedInt(int n) { - if (n < 0) { - return n & 0xffffffffL; - } - return n; - } - - @Specialization(guards = "n >= 0") - static long doUnsignedLongPositive(long n) { - return n; - } - - @Specialization(guards = "n < 0") - static Object doUnsignedLongNegative(long n, - @Bind PythonLanguage language) { - return PFactory.createInt(language, PInt.longToUnsignedBigInteger(n)); - } - } - /** * Converts a Python character (1-element Python string) into a UTF-8 encoded C {@code char}. * According to CPython, we need to encode the whole Python string before we access the first @@ -1174,7 +941,7 @@ static Object doUnsignedLongNegative(long n, */ @GenerateInline(false) // footprint reduction 28 -> 9, inherits non-inlineable execute() @GenerateUncached - public abstract static class AsNativeCharNode extends CExtToNativeNode { + public abstract static class AsNativeCharNode extends Node { public abstract byte executeByte(Object value); @@ -1197,7 +964,7 @@ static byte doGeneric(Object value, * * @see AsNativePrimitiveNode */ - public abstract static class AsFixedNativePrimitiveNode extends CExtToNativeNode { + public abstract static class AsFixedNativePrimitiveNode extends Node { private final int targetTypeSize; private final int signed; @@ -1207,6 +974,8 @@ protected AsFixedNativePrimitiveNode(int targetTypeSize, boolean signed) { this.signed = PInt.intValue(signed); } + public abstract Object execute(Object object); + // Adding specializations for primitives does not make a lot of sense just to avoid // un-/boxing in the interpreter since interop will force un-/boxing anyway. @Specialization @@ -1247,190 +1016,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 a native callable 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..478d40bf56 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, 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,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(long pointer); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtToNativeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtToNativeNode.java index 29e3ba34e4..f655198fab 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtToNativeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtToNativeNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, 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 @@ -44,5 +44,5 @@ public abstract class CExtToNativeNode extends PNodeWithContext { - public abstract Object execute(Object object); + public abstract long 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..30b04b1e73 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,15 +40,30 @@ */ package com.oracle.graal.python.builtins.objects.cext.common; -import com.oracle.truffle.api.strings.TruffleString; +import com.oracle.graal.python.annotations.NativeSimpleType; +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.truffle.api.CompilerDirectives.TruffleBoundary; public interface NativeCExtSymbol { String getName(); - TruffleString getTsName(); + ArgDescriptor getReturnValue(); - /** - * Returns the NFI signature. - */ - String getSignature(); + ArgDescriptor[] getArguments(); + + @TruffleBoundary + 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/copying/ElfFile.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/ElfFile.java index ee6c5c0e65..c813edf84a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/ElfFile.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/ElfFile.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,9 @@ package com.oracle.graal.python.builtins.objects.cext.copying; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; @@ -49,53 +51,112 @@ import com.oracle.truffle.api.TruffleFile; final class ElfFile extends SharedObject { + private static final String PATCHELF_INSTALL_INSTRUCTION = "IsolateNativeModules option needs `patchelf` tool to copy libraries. Make sure you have it available " + + "on PATH or installed in your venv."; + private final PythonContext context; private final TruffleFile tempfile; - private String getPatchelf() { - return which(context, "patchelf").toString(); + private String getPatchelf() throws NativeLibraryToolException { + TruffleFile patchelf = which(context, "patchelf"); + if (!patchelf.exists()) { + throw new NativeLibraryToolException("Could not find `patchelf`. " + PATCHELF_INSTALL_INSTRUCTION); + } + return patchelf.toString(); } - ElfFile(byte[] b, PythonContext context) throws IOException { - this.context = context; - this.tempfile = context.getEnv().createTempFile(null, null, ".so"); - try (var os = this.tempfile.newOutputStream(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { - os.write(b); + private void runPatchelf(String action, String... arguments) throws NativeLibraryToolException { + var command = new String[arguments.length + 1]; + command[0] = getPatchelf(); + System.arraycopy(arguments, 0, command, 1, arguments.length); + var pb = newProcessBuilder(context); + var stderr = new ByteArrayOutputStream(); + pb.redirectError(pb.createRedirectToStream(stderr)); + pb.command(command); + Process proc; + try { + proc = pb.start(); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to start `patchelf` to " + action + ": " + e.getMessage() + ". " + PATCHELF_INSTALL_INSTRUCTION, e); + } + try { + if (proc.waitFor() != 0) { + throw new NativeLibraryToolException("Failed to run `patchelf` to " + action + " (exit code " + proc.exitValue() + "). " + PATCHELF_INSTALL_INSTRUCTION + + " Stderr: " + getStderr(stderr)); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new NativeLibraryToolException("Interrupted while waiting for `patchelf` to " + action + ". " + PATCHELF_INSTALL_INSTRUCTION, e); } } - @Override - public void setId(String newId) throws IOException, InterruptedException { - var pb = newProcessBuilder(context); - pb.command(getPatchelf(), "--debug", "--set-soname", newId, tempfile.toString()); - var proc = pb.start(); - if (proc.waitFor() != 0) { - throw new IOException("Failed to run `patchelf` command. Make sure you have it on your PATH or installed in your venv."); + private static String getStderr(ByteArrayOutputStream stderr) { + String output = stderr.toString(StandardCharsets.UTF_8).strip(); + return output.isEmpty() ? "" : output; + } + + private static void deleteTempfile(TruffleFile tempfile) throws NativeLibraryToolException { + try { + tempfile.delete(); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to delete temporary ELF library copy '" + tempfile + "': " + e.getMessage(), e); } } - @Override - public void changeOrAddDependency(String oldName, String newName) throws IOException, InterruptedException { - var pb = newProcessBuilder(context); - pb.command(getPatchelf(), "--debug", "--remove-needed", oldName, tempfile.toString()); - var proc = pb.start(); - if (proc.waitFor() != 0) { - throw new IOException("Failed to run `patchelf` command. Make sure you have it on your PATH or installed in your venv."); + private static void deleteTempfileAfterFailedInit(TruffleFile tempfile, IOException failure) throws NativeLibraryToolException { + var exception = new NativeLibraryToolException("Failed to write temporary ELF library copy '" + tempfile + "' for IsolateNativeModules relocation: " + failure.getMessage(), failure); + try { + deleteTempfile(tempfile); + } catch (NativeLibraryToolException e) { + exception.addSuppressed(e); + } + throw exception; + } + + private static void writeTempfile(TruffleFile tempfile, byte[] b) throws NativeLibraryToolException { + try (var os = tempfile.newOutputStream(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { + os.write(b); + } catch (IOException e) { + deleteTempfileAfterFailedInit(tempfile, e); } - pb.command(getPatchelf(), "--debug", "--add-needed", newName, tempfile.toString()); - proc = pb.start(); - if (proc.waitFor() != 0) { - throw new IOException("Failed to run `patchelf` command. Make sure you have it on your PATH or installed in your venv."); + } + + private static TruffleFile createTempfile(PythonContext context) throws NativeLibraryToolException { + try { + return context.getEnv().createTempFile(null, null, ".so"); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to create temporary ELF library copy for IsolateNativeModules relocation: " + e.getMessage(), e); } } + ElfFile(byte[] b, PythonContext context) throws NativeLibraryToolException { + this.context = context; + this.tempfile = createTempfile(context); + writeTempfile(tempfile, b); + } + @Override - public void write(TruffleFile copy) throws IOException { - tempfile.copy(copy, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + public void setId(String newId) throws NativeLibraryToolException { + runPatchelf("set SONAME", "--debug", "--set-soname", newId, tempfile.toString()); + } + + @Override + public void changeOrAddDependency(String oldName, String newName) throws NativeLibraryToolException { + runPatchelf("remove dependency", "--debug", "--remove-needed", oldName, tempfile.toString()); + runPatchelf("add dependency", "--debug", "--add-needed", newName, tempfile.toString()); + } + + @Override + public void write(TruffleFile copy) throws NativeLibraryToolException { + try { + tempfile.copy(copy, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to write relocated ELF library copy '" + copy + "': " + e.getMessage(), e); + } } @Override - public void close() throws IOException { - tempfile.delete(); + public void close() throws NativeLibraryToolException { + deleteTempfile(tempfile); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/NativeLibraryLocator.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/NativeLibraryLocator.java index 4366027daf..db4aa1d54f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/NativeLibraryLocator.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/NativeLibraryLocator.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,18 +40,17 @@ */ package com.oracle.graal.python.builtins.objects.cext.copying; +import static com.oracle.graal.python.nodes.StringLiterals.J_MAX_CAPI_COPIES; import static com.oracle.graal.python.nodes.StringLiterals.J_NATIVE; import static com.oracle.graal.python.nodes.StringLiterals.T_BASE_PREFIX; -import static com.oracle.graal.python.nodes.StringLiterals.J_MAX_CAPI_COPIES; import static com.oracle.graal.python.nodes.StringLiterals.T_PREFIX; -import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ApiInitException; -import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ImportException; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; @@ -61,6 +60,7 @@ import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.TruffleLogger; +import com.oracle.truffle.api.strings.TruffleString; /** * Given a GraalPy virtual environment, this class helps prepare that environment so that multiple @@ -127,13 +127,8 @@ public NativeLibraryLocator(PythonContext context, TruffleFile capiLibrary, bool * * @see PythonOptions#IsolateNativeModules */ - public String resolve(PythonContext context, TruffleFile original) throws ImportException { - try { - return resolve(context, original, capiSlot, capiOriginal); - } catch (ApiInitException e) { - throw new ImportException(null, toTruffleStringUncached(original.getName()), toTruffleStringUncached(original.getPath()), - toTruffleStringUncached(e.getMessage() == null ? "" : e.getMessage())); - } + public String resolve(PythonContext context, TruffleFile original) throws ApiInitException { + return resolve(context, original, capiSlot, capiOriginal); } public String getCapiLibrary() { @@ -148,7 +143,7 @@ public void close() { * the same time. The minimum number of concurrent contexts to prepare for is given with {@code * count}. */ - public static void replicate(TruffleFile venvDirectory, PythonContext context, int count) throws IOException, InterruptedException { + public static void replicate(TruffleFile venvDirectory, PythonContext context, int count) throws NativeLibraryToolException { if (count > MAX_CEXT_COPIES) { LOGGER.warning(() -> String.format("The current limit for concurrent Python contexts accessing the Python C API is %d, " + "but we are preparing %d copies. The extra copies will only be used if a different value " + @@ -156,24 +151,13 @@ public static void replicate(TruffleFile venvDirectory, PythonContext context, i } String suffix = context.getSoAbi().toJavaStringUncached(); TruffleFile capiLibrary = context.getPublicTruffleFileRelaxed(context.getCAPIHome()).resolve(PythonContext.getSupportLibName("python-" + J_NATIVE)); - try { - for (int i = 0; i < count; i++) { - // Relocate the C API library - replicate(capiLibrary, venvDirectory.resolve(copyNameOf(capiLibrary.getName(), i)), context, i); - // Relocate the core C extensions - walk(context.getPublicTruffleFileRelaxed(context.getCoreHome()), suffix, capiLibrary.getName(), context, i, (o, n) -> venvDirectory.resolve(n)); - // Relocate C extensions in the venv - walk(venvDirectory, suffix, capiLibrary.getName(), context, i, (o, n) -> o.resolveSibling(n)); - } - } catch (RuntimeException e) { - var cause = e.getCause(); - if (cause instanceof IOException ioCause) { - throw ioCause; - } else if (cause instanceof InterruptedException intCause) { - throw intCause; - } else { - throw e; - } + for (int i = 0; i < count; i++) { + // Relocate the C API library + replicate(capiLibrary, venvDirectory.resolve(copyNameOf(capiLibrary.getName(), i)), context, i); + // Relocate the core C extensions + walk(context.getPublicTruffleFileRelaxed(context.getCoreHome()), suffix, capiLibrary.getName(), context, i, (o, n) -> venvDirectory.resolve(n)); + // Relocate C extensions in the venv + walk(venvDirectory, suffix, capiLibrary.getName(), context, i, (o, n) -> o.resolveSibling(n)); } } @@ -229,14 +213,15 @@ private static String resolve(PythonContext context, TruffleFile original, int c if (!copy.isReadable()) { try { replicate(original, copy, context, capiSlot, capiOrignalName); - } catch (IOException | InterruptedException e) { - throw new ApiInitException(e); + } catch (NativeLibraryToolException e) { + throw new ApiInitException(TruffleString.fromJavaStringUncached(e.getMessage(), TS_ENCODING)); } } return copy.getPath(); } - private static void replicate(TruffleFile original, TruffleFile copy, PythonContext context, int slot, String... dependenciesToUpdate) throws IOException, InterruptedException { + private static void replicate(TruffleFile original, TruffleFile copy, PythonContext context, int slot, String... dependenciesToUpdate) + throws NativeLibraryToolException { try (var o = SharedObject.open(original, context)) { for (var depToUpdate : dependenciesToUpdate) { if (depToUpdate != null) { @@ -250,7 +235,7 @@ private static void replicate(TruffleFile original, TruffleFile copy, PythonCont } private static void walk(TruffleFile dir, String suffix, String capiOriginalName, PythonContext context, int capiSlot, BiFunction f) - throws IOException, InterruptedException { + throws NativeLibraryToolException { try (var ds = dir.newDirectoryStream()) { for (var e : ds) { if (e.isDirectory()) { @@ -259,6 +244,8 @@ private static void walk(TruffleFile dir, String suffix, String capiOriginalName replicate(e, f.apply(e, copyNameOf(e.getName(), capiSlot)), context, capiSlot, capiOriginalName); } } + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to scan native library directory '" + dir + "' for IsolateNativeModules relocation: " + e.getMessage(), e); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/NativeLibraryToolException.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/NativeLibraryToolException.java new file mode 100644 index 0000000000..f3be9f34da --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/NativeLibraryToolException.java @@ -0,0 +1,53 @@ +/* + * 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.builtins.objects.cext.copying; + +public final class NativeLibraryToolException extends Exception { + private static final long serialVersionUID = 5478160327398513738L; + + NativeLibraryToolException(String message) { + super(message); + } + + NativeLibraryToolException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.java index 63cbd39ea3..cde55ce4db 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/PEFile.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,9 @@ package com.oracle.graal.python.builtins.objects.cext.copying; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; @@ -49,23 +51,35 @@ import com.oracle.truffle.api.TruffleFile; final class PEFile extends SharedObject { + private static final String DELVEWHEEL_VERSION = "1.13.0"; + private static final String DELVEWHEEL_INSTALL_INSTRUCTION = "IsolateNativeModules option needs `delvewheel` tool to copy libraries. Make sure you have `delvewheel>=" + DELVEWHEEL_VERSION + + "` available in the virtualenv or on PATH (needs environment access)."; + private final PythonContext context; private final TruffleFile tempfile; - PEFile(byte[] b, PythonContext context) throws IOException { + PEFile(byte[] b, PythonContext context) throws NativeLibraryToolException { this.context = context; - this.tempfile = context.getEnv().createTempFile(null, null, ".dll"); - try (var os = this.tempfile.newOutputStream(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { + TruffleFile temp; + try { + temp = context.getEnv().createTempFile(null, null, ".dll"); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to create temporary PE library copy for IsolateNativeModules relocation: " + e.getMessage(), e); + } + this.tempfile = temp; + try (var os = tempfile.newOutputStream(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { os.write(b); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to write temporary PE library copy '" + tempfile + "' for IsolateNativeModules relocation: " + e.getMessage(), e); } } @Override - public void setId(String newId) throws IOException { + public void setId(String newId) { // TODO } - private String getDelvewheelPython() { + private String getDelvewheel() throws NativeLibraryToolException { TruffleFile delvewheel = which(context, "delvewheel.exe"); if (!delvewheel.exists()) { delvewheel = which(context, "delvewheel.bat"); @@ -73,46 +87,57 @@ private String getDelvewheelPython() { if (!delvewheel.exists()) { delvewheel = which(context, "delvewheel.cmd"); } - TruffleFile python = delvewheel.resolveSibling("python.exe"); - if (!python.exists()) { - python = delvewheel.resolveSibling("python.bat"); - } - if (!python.exists()) { - python = delvewheel.resolveSibling("python.cmd"); - } - if (!python.exists()) { - python = delvewheel.getParent().resolveSibling("python.exe"); - } - if (!python.exists()) { - python = delvewheel.getParent().resolveSibling("python.bat"); - } - if (!python.exists()) { - python = delvewheel.getParent().resolveSibling("python.cmd"); + if (!delvewheel.exists()) { + throw new NativeLibraryToolException("Could not find `delvewheel`. " + DELVEWHEEL_INSTALL_INSTRUCTION); } - return python.toString(); + return delvewheel.toString(); } @Override - public void changeOrAddDependency(String oldName, String newName) throws IOException, InterruptedException { + public void changeOrAddDependency(String oldName, String newName) throws NativeLibraryToolException { var pb = newProcessBuilder(context); - var tempfileWithForwardSlashes = tempfile.toString().replace('\\', '/'); - String pythonExe = getDelvewheelPython(); - pb.command(pythonExe, "-c", - String.format("from delvewheel import _dll_utils; _dll_utils.replace_needed('%s', ['%s'], {'%s': '%s'}, strip=True, verbose=2, test=[])", - tempfileWithForwardSlashes, oldName, oldName, newName)); - var proc = pb.start(); - if (proc.waitFor() != 0) { - throw new IOException("Failed to run `delvewheel` 1.9.0 to copy required DLL. Make sure you have it installed in your venv."); + var stderr = new ByteArrayOutputStream(); + pb.redirectError(pb.createRedirectToStream(stderr)); + String delvewheel = getDelvewheel(); + pb.command(delvewheel, "replace-needed", "-v", "-v", "--strip", "-change", oldName, newName, + tempfile.toString()); + Process proc; + try { + proc = pb.start(); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to start `delvewheel` to copy required DLL: " + e.getMessage() + ". " + DELVEWHEEL_INSTALL_INSTRUCTION, e); + } + try { + if (proc.waitFor() != 0) { + throw new NativeLibraryToolException("Failed to run `delvewheel` to copy required DLL (exit code " + proc.exitValue() + "). " + DELVEWHEEL_INSTALL_INSTRUCTION + + " Stderr: " + getStderr(stderr)); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new NativeLibraryToolException("Interrupted while waiting for `delvewheel` to copy required DLL. " + DELVEWHEEL_INSTALL_INSTRUCTION, e); } } + private static String getStderr(ByteArrayOutputStream stderr) { + String output = stderr.toString(StandardCharsets.UTF_8).strip(); + return output.isEmpty() ? "" : output; + } + @Override - public void write(TruffleFile copy) throws IOException { - tempfile.copy(copy, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + public void write(TruffleFile copy) throws NativeLibraryToolException { + try { + tempfile.copy(copy, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to write relocated PE library copy '" + copy + "': " + e.getMessage(), e); + } } @Override - public void close() throws IOException { - tempfile.delete(); + public void close() throws NativeLibraryToolException { + try { + tempfile.delete(); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to delete temporary PE library copy '" + tempfile + "': " + e.getMessage(), e); + } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/SharedObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/SharedObject.java index 0fd42baffd..2876aad951 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/SharedObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/copying/SharedObject.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,25 +51,30 @@ import com.oracle.truffle.api.io.TruffleProcessBuilder; abstract class SharedObject implements AutoCloseable { - abstract void setId(String newId) throws IOException, InterruptedException; + abstract void setId(String newId) throws NativeLibraryToolException; - abstract void changeOrAddDependency(String oldName, String newName) throws IOException, InterruptedException; + abstract void changeOrAddDependency(String oldName, String newName) throws NativeLibraryToolException; - abstract void write(TruffleFile copy) throws IOException, InterruptedException; + abstract void write(TruffleFile copy) throws NativeLibraryToolException; - public abstract void close() throws IOException; + public abstract void close() throws NativeLibraryToolException; - static SharedObject open(TruffleFile file, PythonContext context) throws IOException { - var f = file.readAllBytes(); + static SharedObject open(TruffleFile file, PythonContext context) throws NativeLibraryToolException { + byte[] f; + try { + f = file.readAllBytes(); + } catch (IOException e) { + throw new NativeLibraryToolException("Failed to read native library '" + file + "' for IsolateNativeModules relocation: " + e.getMessage(), e); + } switch (f[0]) { case 0x7f: return new ElfFile(f, context); case 0x4d, 0x5a: return new PEFile(f, context); case (byte) 0xca, (byte) 0xfe, (byte) 0xce, (byte) 0xcf: - throw new IOException("Modifying Mach-O files is not yet supported"); + throw new NativeLibraryToolException("Failed to relocate native library '" + file + "': modifying Mach-O files is not yet supported."); default: - throw new IOException("Unknown shared object format"); + throw new NativeLibraryToolException("Failed to relocate native library '" + file + "': unknown shared object format."); } } 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..ca176b7dd6 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,13 @@ 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.NativeCAPISymbol; +import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -55,7 +55,7 @@ /** * Helper enum to extract constants from the C space. Constants are limited to the range of "long" - * values, but '-1' is not allowed at the moment. + * values. */ @CApiConstants public enum CConstants { @@ -67,12 +67,15 @@ public enum CConstants { @CompilationFinal(dimensions = 1) public static final CConstants[] VALUES = values(); - @CompilationFinal private long longValue = -1; - @CompilationFinal private int intValue = -1; + private static final long UNRESOLVED = Long.MIN_VALUE; + private static final int INT_OVERFLOW = Integer.MIN_VALUE; + + @CompilationFinal private long longValue = UNRESOLVED; + @CompilationFinal private int intValue = INT_OVERFLOW; public long longValue() { long o = longValue; - if (o == -1) { + if (o == UNRESOLVED) { CompilerDirectives.transferToInterpreterAndInvalidate(); resolve(); return longValue; @@ -86,10 +89,10 @@ public long longValue() { */ public int intValue() { int o = intValue; - if (o == -1) { + if (o == INT_OVERFLOW) { CompilerDirectives.transferToInterpreterAndInvalidate(); resolve(); - if (intValue == -1) { + if (intValue == INT_OVERFLOW) { throw PRaiseNode.raiseStatic(null, SystemError, INTERNAL_INT_OVERFLOW); } return intValue; @@ -99,13 +102,10 @@ 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 = CApiContext.getNativeCAPIMetadataPointer(null); + long[] constants = readLongArrayElements(constantsPointer, 0L, VALUES.length); for (CConstants constant : VALUES) { constant.longValue = constants[constant.ordinal()]; - if (constant.longValue == -1) { - throw PRaiseNode.raiseStatic(null, SystemError, toTruffleStringUncached("internal limitation - cannot extract constants with value '-1'")); - } if ((constant.longValue & 0xFFFF0000L) == 0xDEAD0000L) { throw PRaiseNode.raiseStatic(null, SystemError, toTruffleStringUncached("marker value reached, regenerate C code (mx python-capi)")); } 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..f534d61709 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 @@ -67,6 +67,7 @@ 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.UCHAR; 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.UINT64_T; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UNSIGNED_INT; 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.allocfunc; @@ -101,14 +102,15 @@ 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.NativeCAPISymbol; +import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; @CApiFields public enum CFields { @@ -121,6 +123,11 @@ public enum CFields { GraalPyVarObject__ob_size(Py_ssize_t), GraalPyVarObject__ob_item(PyObjectPtr), GraalPyFloatObject__ob_fval(Double), + GraalPyUnicodeObject__length(Py_ssize_t), + GraalPyUnicodeObject__byte_length(Py_ssize_t), + GraalPyUnicodeObject__hash(Py_hash_t), + GraalPyUnicodeObject__state(UINT64_T), + GraalPyUnicodeObject__data(Pointer), PyModuleDef__m_name(ConstCharPtr), PyModuleDef__m_doc(ConstCharPtr), @@ -353,11 +360,21 @@ public enum CFields { PyThreadState__current_exception(PyObject), PyThreadState__dict(PyObject), PyThreadState__small_ints(PyObjectPtr), + PyThreadState__singletons(Pointer), PyThreadState__gc(Pointer), + PyThreadState__graalpy_deallocating(Pointer), PyThreadState__py_recursion_limit(Int), PyThreadState__py_recursion_remaining(Int), PyThreadState__c_recursion_remaining(Int), + GraalPySingletons__tuple_empty(PyObject), + GraalPySingletons__bytes_empty(PyObject), + GraalPySingletons__bytes_characters(PyObjectPtr), + + GraalPyDeallocState__items(PyObjectPtr), + GraalPyDeallocState__len(Int), + GraalPyDeallocState__capacity(Int), + GCState__enabled(Int), GCState__debug(Int), GCState__generations(Pointer), @@ -377,7 +394,16 @@ public enum CFields { PyBaseExceptionObject__traceback(PyObject), PyBaseExceptionObject__context(PyObject), PyBaseExceptionObject__cause(PyObject), - PyBaseExceptionObject__suppress_context(CHAR); + PyBaseExceptionObject__suppress_context(CHAR), + + PySyntaxErrorObject__msg(PyObject), + PySyntaxErrorObject__filename(PyObject), + PySyntaxErrorObject__lineno(PyObject), + PySyntaxErrorObject__offset(PyObject), + PySyntaxErrorObject__end_lineno(PyObject), + PySyntaxErrorObject__end_offset(PyObject), + PySyntaxErrorObject__text(PyObject), + PySyntaxErrorObject__print_file_and_line(PyObject); public static final int PyASCIIObject__state_interned_shift = 0; public static final int PyASCIIObject__state_kind_shift = 2; @@ -398,7 +424,7 @@ public enum CFields { @CompilationFinal private long offset = -1; - long offset() { + public long offset() { long o = offset; if (o == -1) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -412,10 +438,11 @@ CStructs struct() { return struct; } + @InliningCutoff 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 = CApiContext.getNativeCAPIMetadataPointer(null) + (CConstants.VALUES.length + 1L) * Long.BYTES; + 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..7187290021 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,273 @@ */ 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.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.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.capi.transitions.CApiTransitions.PythonToNativeInternalNode; 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.graal.python.runtime.PythonContext; 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); - } + public static byte readByteField(long structBasePtr, CFields field) { + assert field.type.isI8(); + return NativeMemory.readByte(getFieldPtr(structBasePtr, field)); + } - abstract void execute(Object pointer); + public static void writeByteField(long structBasePtr, CFields field, byte value) { + assert field.type.isI8(); + NativeMemory.writeByte(getFieldPtr(structBasePtr, field), value); + } - public final void free(Object pointer) { - execute(pointer); - } + public static int readIntField(long structBasePtr, CFields field) { + assert field.type.isI32(); + return NativeMemory.readInt(getFieldPtr(structBasePtr, field)); + } - @Specialization - public static void freeLong(long pointer) { - UNSAFE.freeMemory(pointer); - } + public static void writeIntField(long structBasePtr, CFields field, int value) { + assert field.type.isI32(); + NativeMemory.writeInt(getFieldPtr(structBasePtr, field), value); + } - @Specialization - static void freeNativePointer(NativePointer pointer) { - UNSAFE.freeMemory(pointer.asPointer()); - } + public static int readStructArrayIntField(long arrayPtr, long index, CFields field) { + return readIntField(getArrayElementPtr(arrayPtr, index, field.struct), field); + } - @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3") - static void freePointer(Object pointer, - @CachedLibrary("pointer") InteropLibrary lib) { - UNSAFE.freeMemory(asPointer(pointer, lib)); - } + public static void writeStructArrayIntField(long arrayPtr, long index, CFields field, int value) { + writeIntField(getArrayElementPtr(arrayPtr, index, field.struct), field, value); + } - @NeverDefault - public static FreeNode create() { - return FreeNodeGen.create(); - } + public static long readLongField(long structBasePtr, CFields field) { + assert field.type.isI64(); + return NativeMemory.readLong(getFieldPtr(structBasePtr, field)); + } - public static FreeNode getUncached() { - return FreeNodeGen.getUncached(); - } + public static void writeLongField(long structBasePtr, CFields field, long value) { + assert field.type.isI64(); + NativeMemory.writeLong(getFieldPtr(structBasePtr, field), value); } - public abstract static class ReadBaseNode extends Node implements CStructAccessNode { + public static long readStructArrayLongField(long arrayPtr, long index, CFields field) { + return readLongField(getArrayElementPtr(arrayPtr, index, field.struct), field); + } - abstract Object executeGeneric(Object pointer, long offset); + public static void writeStructArrayLongField(long arrayPtr, long index, CFields field, long value) { + writeLongField(getArrayElementPtr(arrayPtr, index, field.struct), field, value); + } - public final Object readGeneric(Object pointer, long offset) { - return executeGeneric(pointer, offset); - } + public static double readDoubleField(long structBasePtr, CFields field) { + assert field.type.isDouble(); + return NativeMemory.readDouble(getFieldPtr(structBasePtr, field)); } - @ImportStatic(PGuards.class) - @GenerateUncached - @GenerateInline(false) - public abstract static class GetElementPtrNode extends ReadBaseNode { + public static void writeDoubleField(long structBasePtr, CFields field, double value) { + assert field.type.isDouble(); + NativeMemory.writeDouble(getFieldPtr(structBasePtr, field), value); + } - abstract Object execute(Object pointer, long offset); + public static long readPtrField(long structBasePtr, CFields field) { + assert field.type.isPyObjectOrPointer(); + return NativeMemory.readLong(getFieldPtr(structBasePtr, field)); + } - public final Object getElementPtr(Object pointer, CFields field) { - assert accepts(field); - return execute(pointer, field.offset()); - } + public static void writePtrField(long structBasePtr, CFields field, long value) { + assert field.type.isPyObjectOrPointer(); + NativeMemory.writeLong(getFieldPtr(structBasePtr, field), value); + } - public final boolean accepts(ArgDescriptor desc) { - return true; - } + public static long readStructArrayPtrField(long arrayPtr, long index, CFields field) { + return readPtrField(getArrayElementPtr(arrayPtr, index, field.struct), field); + } - @Specialization - static long getLong(long pointer, long offset) { - return pointer + offset; - } + public static void writeStructArrayPtrField(long arrayPtr, long index, CFields field, long value) { + writePtrField(getArrayElementPtr(arrayPtr, index, field.struct), field, value); + } - @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); - } + private static long getArrayElementPtr(long arrayPtr, long index, CStructs struct) { + return arrayPtr + index * struct.size(); + } - @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 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); } + } - public static GetElementPtrNode getUncached() { - return GetElementPtrNodeGen.getUncached(); - } + public static long allocatePyMem(int size) { + return allocatePyMem(1, size); } @ImportStatic(PGuards.class) @GenerateUncached @GenerateInline(false) - public abstract static class ReadByteNode extends ReadBaseNode { - - abstract byte execute(Object pointer, long offset); + public abstract static class ReadObjectNode extends Node { + abstract Object execute(long 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); + public final Object read(long pointer, CFields field) { + assert field.type.isPyObject(); 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 Object read(long pointer, long offset) { + return execute(pointer, offset); } - public final boolean accepts(ArgDescriptor desc) { - return desc.isI8(); + public final Object readFromObj(PythonNativeObject self, CFields field) { + return read(self.getPtr(), field); } - public final byte[] readByteArray(Object pointer, int elements) { - return readByteArray(pointer, elements, 0); + public final Object[] readPyObjectArray(long pointer, int elements) { + return readPyObjectArray(pointer, elements, 0); } - public final byte[] readByteArray(Object pointer, int elements, long sourceOffset) { - byte[] result = new byte[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 + sourceOffset) * Byte.BYTES); + result[i] = execute(pointer, (i + offset) * POINTER_SIZE); } 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 final byte readArrayElement(Object pointer, long element) { - return execute(pointer, element); - } - - @Specialization - static byte readLong(long pointer, long offset) { - assert offset >= 0; - return UNSAFE.getByte(pointer + offset); - } - - @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); - } - - @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); - } - - /** - * 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 ReadByteNode getUncached() { - return CStructAccessFactory.ReadByteNodeGen.getUncached(); - } - } - - @ImportStatic(PGuards.class) - @GenerateUncached - @GenerateInline(false) - public abstract static class ReadI16Node extends ReadBaseNode { - - abstract int execute(Object pointer, long offset); - - public final int read(Object pointer, CFields field) { - assert accepts(field); - return execute(pointer, field.offset()); - } - - public final boolean accepts(ArgDescriptor desc) { - return desc.isI16(); - } - - public final int readOffset(Object pointer, long offset) { - return execute(pointer, offset); - } - - public final int readArrayElement(Object pointer, long element) { - return execute(pointer, element * Short.BYTES); - } - @Specialization - static int readLong(long pointer, long offset) { + static Object readLong(long pointer, long offset, + @Cached NativePtrToPythonNode toPython) { assert offset >= 0; - return UNSAFE.getShort(pointer + offset); + return toPython.execute(NativeMemory.readPtr(pointer + offset), false); } - @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_SHORT_MEMBER, pointer, offset); + public static ReadObjectNode getUncached() { + return ReadObjectNodeGen.getUncached(); } } @ImportStatic(PGuards.class) @GenerateUncached @GenerateInline(false) - public abstract static class ReadI32Node extends ReadBaseNode { + public abstract static class ReadCharPtrNode extends Node { + abstract TruffleString execute(long pointer, CFields field); - public static int readUncached(Object pointer, CFields field) { - return ReadI32NodeGen.getUncached().read(pointer, field); + public static TruffleString executeUncached(long pointer, CFields field) { + return ReadCharPtrNodeGen.getUncached().execute(pointer, field); } - abstract int execute(Object pointer, long offset); - - public final int read(Object pointer, CFields field) { - assert accepts(field); - 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) { - 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()); + public final TruffleString readFromObj(PythonNativeObject self, CFields field) { + return execute(self.getPtr(), field); } @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(); + 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 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) { - 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 long[] readLongArray(Object pointer, int elements, int offset) { - long[] result = new long[elements]; - for (int i = 0; i < result.length; i++) { - result[i] = execute(pointer, (i + offset) * POINTER_SIZE); - } - return result; - } + public abstract static class WriteTruffleStringNode extends Node { - public final int[] readLongAsIntArray(Object pointer, int elements) { - return readLongAsIntArray(pointer, elements, 0); - } + abstract void execute(long dstPointer, int dstOffset, TruffleString src, int srcOffset, int length, TruffleString.Encoding encoding); - 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; + @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 long readArrayElement(Object pointer, long element) { - return execute(pointer, element * POINTER_SIZE); + public final void write(long dstPointer, TruffleString src, TruffleString.Encoding encoding) { + execute(dstPointer, 0, src, 0, src.byteLength(encoding), encoding); } - public final long readStructArrayElement(Object pointer, long element, CFields field) { - assert accepts(field); - return execute(pointer, element * field.struct.size() + field.offset()); + @TruffleBoundary + public static void writeUncached(long dstPointer, TruffleString src, TruffleString.Encoding encoding) { + executeUncached(dstPointer, 0, src, 0, src.byteLength(encoding), encoding); } @Specialization - static long readLong(long pointer, long offset) { - 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); - } - - @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(); + 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); } } - /** - * 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 { - - 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 abstract static class WriteObjectNewRefNode extends Node { - public final boolean accepts(ArgDescriptor desc) { - return desc.isDouble(); - } + abstract void execute(long pointer, long offset, Object value); - public final double readArrayElement(Object pointer, int element) { - return execute(pointer, element * Float.BYTES); + public final void write(long pointer, CFields field, Object value) { + assert field.type.isPyObject(); + execute(pointer, field.offset(), value); } - @Specialization - static double readLong(long pointer, long offset) { - assert offset >= 0; - return UNSAFE.getFloat(pointer + offset); + public final void writeToObject(PythonNativeObject self, CFields field, Object value) { + write(self.getPtr(), field, value); } - @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); + public final void write(long pointer, Object value) { + execute(pointer, 0, value); } - @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; + public final void writeArray(long pointer, Object[] values, int length, int sourceOffset, long targetOffset) { + if (length > values.length) { + throw shouldNotReachHere(); } - try { - return resultLib.asFloat(result); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(); + for (int i = 0; i < length; i++) { + execute(pointer, (i + targetOffset) * POINTER_SIZE, values[i + sourceOffset]); } } - public static ReadFloatNode getUncached() { - return CStructAccessFactory.ReadFloatNodeGen.getUncached(); - } - - @NeverDefault - public static ReadFloatNode create() { - return CStructAccessFactory.ReadFloatNodeGen.create(); - } - } - - @ImportStatic(PGuards.class) - @GenerateUncached - @GenerateInline(false) - public abstract static class ReadDoubleNode extends ReadBaseNode { - - 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 final double readArrayElement(Object pointer, int element) { - return execute(pointer, element * Double.BYTES); - } - @Specialization - static double readLong(long pointer, long offset) { + static void writeLong(long pointer, long offset, Object value, + @Bind Node inliningTarget, + @Cached NativePtrToPythonNode toPython, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode toNative) { 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(); + long old = NativeMemory.readPtr(pointer + offset); + if (old != NULLPTR) { + toPython.execute(old, true); } - } - - public static ReadDoubleNode getUncached() { - return CStructAccessFactory.ReadDoubleNodeGen.getUncached(); - } - - @NeverDefault - public static ReadDoubleNode create() { - return CStructAccessFactory.ReadDoubleNodeGen.create(); + Object promoted = ensurePythonObjectNode.execute(PythonContext.get(inliningTarget), value, false); + NativeMemory.writePtr(pointer + offset, toNative.executeNewRef(inliningTarget, promoted)); } } - - @ImportStatic(PGuards.class) - @GenerateUncached - @GenerateInline(false) - public abstract static class ReadPointerNode 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 static Object readUncached(Object pointer, CFields field) { - return getUncached().read(pointer, field); - } - - public final Object readFromObj(PythonNativeObject self, CFields field) { - return read(self.getPtr(), field); - } - - public final boolean accepts(ArgDescriptor desc) { - return desc.isPyObjectOrPointer(); - } - - public final Object readArrayElement(Object pointer, long element) { - return execute(pointer, element * POINTER_SIZE); - } - - public final Object readStructArrayElement(Object pointer, long element, CFields field) { - assert accepts(field); - return execute(pointer, element * field.struct.size() + field.offset()); - } - - @Specialization - static Object readLong(long pointer, long offset) { - 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) { - 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); - } - } - - 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..fff41f0c71 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,9 +40,10 @@ */ 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.NativeCAPISymbol; +import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; @@ -72,6 +73,7 @@ public enum CStructs { PyTupleObject, PyFloatObject, GraalPyFloatObject, + GraalPyUnicodeObject, _PyLongValue, PyLongObject, PyModuleDef_Base, @@ -84,18 +86,22 @@ public enum CStructs { PyASCIIObject, PyCompactUnicodeObject, PyBaseExceptionObject, + PySyntaxErrorObject, PyUnicodeObject, Py_UNICODE, PyGetSetDef, PyMemberDef, PyThreadState, + GraalPySingletons, + GraalPyDeallocState, wchar_t, long__long, Py_ssize_t, GCState, PyGC_Head, GraalPyGC_CycleNode, - GCGeneration; + GCGeneration, + GraalPy_Test_CAPI; @CompilationFinal(dimensions = 1) public static final CStructs[] VALUES = values(); @@ -113,8 +119,8 @@ 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 = CApiContext.getNativeCAPIMetadataPointer(null) + (CConstants.VALUES.length + CFields.VALUES.length + 2L) * Long.BYTES; + 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/CodeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java index 3e663b23ce..dbf62ab8f0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeBuiltins.java @@ -49,9 +49,9 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary; -import com.oracle.graal.python.builtins.objects.bytes.PBytes; +import com.oracle.graal.python.builtins.objects.bytes.BytesNodes.GetBytesStorage; import com.oracle.graal.python.builtins.objects.code.CodeBuiltinsClinicProviders.CodeConstructorNodeClinicProviderGen; -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.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -61,12 +61,15 @@ import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.compiler.OpCodes; import com.oracle.graal.python.compiler.SourceMap; +import com.oracle.graal.python.lib.PyBytesCheckNode; import com.oracle.graal.python.lib.PyObjectGetIter; import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; 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.PRaiseNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode; @@ -124,23 +127,32 @@ public abstract static class CodeConstructorNode extends PythonClinicBuiltinNode static PCode call(VirtualFrame frame, @SuppressWarnings("unused") Object cls, int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, - PBytes codestring, PTuple constants, PTuple names, PTuple varnames, + Object codestring, Object constants, Object names, Object varnames, TruffleString filename, TruffleString name, TruffleString qualname, - int firstlineno, PBytes linetable, @SuppressWarnings("unused") PBytes exceptiontable, - PTuple freevars, PTuple cellvars, + int firstlineno, Object linetable, Object exceptiontable, + Object freevars, Object cellvars, @Bind Node inliningTarget, @CachedLibrary(limit = "1") PythonBufferAccessLibrary bufferLib, @Cached CodeNodes.CreateCodeNode createCodeNode, - @Cached SequenceNodes.GetObjectArrayNode getObjectArrayNode, + @Cached GetBytesStorage getBytesStorage, + @Cached GetTupleStorage getTupleStorage, + @Cached SequenceStorageNodes.ToArrayNode toArrayNode, + @Cached PyBytesCheckNode bytesCheckNode, + @Cached PyTupleCheckNode tupleCheckNode, @Cached CastToTruffleStringNode castToTruffleStringNode) { - byte[] codeBytes = bufferLib.getCopiedByteArray(codestring); - byte[] linetableBytes = bufferLib.getCopiedByteArray(linetable); - - Object[] constantsArr = getObjectArrayNode.execute(inliningTarget, constants); - TruffleString[] namesArr = objectArrayToTruffleStringArray(inliningTarget, getObjectArrayNode.execute(inliningTarget, names), castToTruffleStringNode); - TruffleString[] varnamesArr = objectArrayToTruffleStringArray(inliningTarget, getObjectArrayNode.execute(inliningTarget, varnames), castToTruffleStringNode); - TruffleString[] freevarsArr = objectArrayToTruffleStringArray(inliningTarget, getObjectArrayNode.execute(inliningTarget, freevars), castToTruffleStringNode); - TruffleString[] cellcarsArr = objectArrayToTruffleStringArray(inliningTarget, getObjectArrayNode.execute(inliningTarget, cellvars), castToTruffleStringNode); + byte[] codeBytes = getBytes(inliningTarget, codestring, bytesCheckNode, getBytesStorage, bufferLib); + byte[] linetableBytes = getBytes(inliningTarget, linetable, bytesCheckNode, getBytesStorage, bufferLib); + checkBytes(inliningTarget, exceptiontable, bytesCheckNode); + + Object[] constantsArr = getTupleArray(inliningTarget, constants, tupleCheckNode, getTupleStorage, toArrayNode); + TruffleString[] namesArr = objectArrayToTruffleStringArray(inliningTarget, + getTupleArray(inliningTarget, names, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode); + TruffleString[] varnamesArr = objectArrayToTruffleStringArray(inliningTarget, + getTupleArray(inliningTarget, varnames, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode); + TruffleString[] freevarsArr = objectArrayToTruffleStringArray(inliningTarget, + getTupleArray(inliningTarget, freevars, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode); + TruffleString[] cellcarsArr = objectArrayToTruffleStringArray(inliningTarget, + getTupleArray(inliningTarget, cellvars, tupleCheckNode, getTupleStorage, toArrayNode), castToTruffleStringNode); return createCodeNode.execute(frame, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, @@ -150,15 +162,27 @@ static PCode call(VirtualFrame frame, @SuppressWarnings("unused") Object cls, in firstlineno, linetableBytes); } - @Fallback - @SuppressWarnings("unused") - static PCode call(Object cls, Object argcount, Object kwonlyargcount, Object posonlyargcount, - Object nlocals, Object stacksize, Object flags, - Object codestring, Object constants, Object names, Object varnames, - Object filename, Object name, Object qualname, - Object firstlineno, Object linetable, Object exceptiontable, - Object freevars, Object cellvars, - @Bind Node inliningTarget) { + private static byte[] getBytes(Node inliningTarget, Object object, PyBytesCheckNode bytesCheckNode, + GetBytesStorage getBytesStorage, PythonBufferAccessLibrary bufferLib) { + checkBytes(inliningTarget, object, bytesCheckNode); + return bufferLib.getCopiedByteArray(getBytesStorage.execute(inliningTarget, object)); + } + + private static void checkBytes(Node inliningTarget, Object object, PyBytesCheckNode bytesCheckNode) { + if (!bytesCheckNode.execute(inliningTarget, object)) { + throw invalidArgs(inliningTarget); + } + } + + private static Object[] getTupleArray(Node inliningTarget, Object object, PyTupleCheckNode tupleCheckNode, + GetTupleStorage getTupleStorage, SequenceStorageNodes.ToArrayNode toArrayNode) { + if (!tupleCheckNode.execute(inliningTarget, object)) { + throw invalidArgs(inliningTarget); + } + return toArrayNode.execute(inliningTarget, getTupleStorage.execute(inliningTarget, object)); + } + + private static RuntimeException invalidArgs(Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.INVALID_ARGS, "code"); } @@ -396,7 +420,7 @@ static Object lines(PCode self) { } private static List computeLinesForBytecodeDSLInterpreter(PBytecodeDSLRootNode root) { - BytecodeNode bytecodeNode = root.getBytecodeNode(); + BytecodeNode bytecodeNode = root.getBytecodeNode().ensureSourceInformation(); List triples = new ArrayList<>(); SourceInformationTree sourceInformationTree = bytecodeNode.getSourceInformationTree(); assert sourceInformationTree.getSourceSection() != null; @@ -438,7 +462,7 @@ private static void traverseSourceInformationTree(SourceInformationTree tree, Li /** * The bci ranges in the triples are not stable and can change when the bytecode is * instrumented. We create new triples with stable instruction indices by walking the - * instructions. + * instructions. We also coalesce triples with the same line numbers. */ private static List convertTripleBcisToInstructionIndices(BytecodeNode bytecodeNode, PythonLanguage language, List triples) { List result = new ArrayList<>(triples.size()); @@ -448,33 +472,33 @@ private static List convertTripleBcisToInstructionIndices(BytecodeNode b int startInstructionIndex = 0; int instructionIndex = 0; - boolean wasLastInstructionInstrumentation = false; - int lastTripleLine = -1; + int pendingLine = triple[2]; + for (Instruction instruction : bytecodeNode.getInstructions()) { + // Iterate the instructions, counting the stable instruction index as we go. if (instruction.getBytecodeIndex() == triple[1] /* end bci */) { - if (lastTripleLine != triple[2]) { - if (!wasLastInstructionInstrumentation) { - result.add(PFactory.createTuple(language, new int[]{startInstructionIndex, instructionIndex, triple[2]})); - lastTripleLine = triple[2]; - } - startInstructionIndex = instructionIndex; - } + // We hit the end of the current triple. Continue with the next one. triple = triples.get(++tripleIndex); assert triple[0] == instruction.getBytecodeIndex() : "bytecode ranges should be consecutive"; + // If this new triple has a different line, emit a tuple for the previous line. + // Otherwise, continue (this new triple's range is combined with the previous). + if (pendingLine != triple[2] /* line */) { + result.add(PFactory.createTuple(language, new int[]{startInstructionIndex, instructionIndex, pendingLine})); + startInstructionIndex = instructionIndex; + pendingLine = triple[2]; + } } if (!instruction.isInstrumentation()) { // Emulate CPython's fixed 2-word instructions. instructionIndex += 2; - wasLastInstructionInstrumentation = false; - } else { - wasLastInstructionInstrumentation = true; } } - result.add(PFactory.createTuple(language, new int[]{startInstructionIndex, instructionIndex, triple[2]})); + // Emit a tuple for the remaining range. + result.add(PFactory.createTuple(language, new int[]{startInstructionIndex, instructionIndex, pendingLine})); assert tripleIndex == triples.size() - 1 : String.format("every bytecode range should have been converted to " + - "an instruction range, %d != %d, function: %s", tripleIndex, triples.size(), bytecodeNode.getRootNode()); + "an instruction range, %d != %d, function: %s", tripleIndex, triples.size() - 1, bytecodeNode.getRootNode()); return result; } @@ -495,7 +519,11 @@ Object positions(PCode self) { List lines = new ArrayList<>(); if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { PBytecodeDSLRootNode rootNode = (PBytecodeDSLRootNode) self.getRootNodeForExtraction(); - for (Instruction instruction : rootNode.getBytecodeNode().getInstructions()) { + BytecodeNode bytecodeNode = rootNode.getBytecodeNode(); + if (!bytecodeNode.hasSourceInformation()) { + bytecodeNode = bytecodeNode.ensureSourceInformation(); + } + for (Instruction instruction : bytecodeNode.getInstructions()) { if (instruction.isInstrumentation()) { // Skip instrumented instructions. The co_positions array should agree // with the logical instruction index. 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 b61beaa410..5eacf40c31 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 @@ -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 @@ -44,7 +44,6 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.modules.MarshalModuleBuiltins; -import com.oracle.graal.python.builtins.objects.code.CodeNodesFactory.GetCodeRootNodeGen; import com.oracle.graal.python.builtins.objects.function.PFunction; import com.oracle.graal.python.builtins.objects.function.Signature; import com.oracle.graal.python.compiler.BytecodeCodeUnit; @@ -60,7 +59,6 @@ import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.graal.python.util.LazySource; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -83,6 +81,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, @@ -90,12 +102,24 @@ 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, context, argcount, + return createCode(language, argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, codedata, constants, names, varnames, freevars, cellvars, filename, name, qualname, firstlineno, linetable); @@ -105,7 +129,7 @@ public PCode execute(VirtualFrame frame, int argcount, } @TruffleBoundary - private static PCode createCode(PythonLanguage language, PythonContext context, int argCount, + private static PCode createCode(PythonLanguage language, int argCount, int positionalOnlyArgCount, int kwOnlyArgCount, int nlocals, int stacksize, int flags, byte[] codedata, Object[] constants, TruffleString[] names, @@ -116,10 +140,10 @@ private static PCode createCode(PythonLanguage language, PythonContext context, TruffleString name = PythonUtils.internString(uninternedName); qualname = PythonUtils.internString(qualname); - RootCallTarget ct; + PRootNode rootNode; Signature signature; if (codedata.length == 0) { - ct = language.createCachedCallTarget(l -> new BadOPCodeNode(l, name), BadOPCodeNode.class, name.toJavaStringUncached()); + rootNode = language.createCachedRootNode(l -> new BadOPCodeNode(l, name), BadOPCodeNode.class, name.toJavaStringUncached()); /* * We need to create a proper signature because this code path is used to create * fake code objects for duck-typed function-like objects, such as Cython functions. @@ -140,18 +164,16 @@ private static PCode createCode(PythonLanguage language, PythonContext context, varArgsIndex, parameterNames, kwOnlyNames); + return PFactory.createCode(language, rootNode, signature, nlocals, stacksize, flags, constants, names, varnames, freevars, cellvars, filename, name, qualname, firstlineno, linetable); } else { - ct = deserializeForBytecodeInterpreter(context, codedata, cellvars, freevars, flags); - signature = ((PRootNode) ct.getRootNode()).getSignature(); - } - if (filename != null) { - context.setCodeFilename(ct, filename); + rootNode = deserializeForBytecodeInterpreter(language, codedata, cellvars, freevars, flags); + signature = rootNode.getSignature(); } - return PFactory.createCode(language, ct, signature, nlocals, stacksize, flags, constants, names, varnames, freevars, cellvars, filename, name, qualname, firstlineno, linetable); + return PFactory.createCode(language, rootNode, signature, nlocals, stacksize, flags, constants, names, varnames, freevars, cellvars, filename, name, qualname, firstlineno, linetable); } - private static RootCallTarget deserializeForBytecodeInterpreter(PythonContext context, byte[] data, TruffleString[] cellvars, TruffleString[] freevars, int flags) { - CodeUnit codeUnit = MarshalModuleBuiltins.deserializeCodeUnit(null, context, data); + private static PRootNode deserializeForBytecodeInterpreter(PythonLanguage language, byte[] data, TruffleString[] cellvars, TruffleString[] freevars, int flags) { + CodeUnit codeUnit = MarshalModuleBuiltins.deserializeCodeUnit(null, language, data); RootNode rootNode; if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { @@ -159,7 +181,7 @@ private static RootCallTarget deserializeForBytecodeInterpreter(PythonContext co if (code.flags != flags) { code = code.withFlags(flags); } - rootNode = code.createRootNode(context, PythonUtils.createFakeSource()); + rootNode = code.createRootNode(language, false); } else { BytecodeCodeUnit code = (BytecodeCodeUnit) codeUnit; if (cellvars != null && !Arrays.equals(code.cellvars, cellvars) || freevars != null && !Arrays.equals(code.freevars, freevars) || flags != code.flags) { @@ -171,12 +193,12 @@ private static RootCallTarget deserializeForBytecodeInterpreter(PythonContext co code.variableShouldUnbox, code.generalizeInputsKeys, code.generalizeInputsIndices, code.generalizeInputsValues, code.generalizeVarsIndices, code.generalizeVarsValues); } - rootNode = PBytecodeRootNode.create(context.getLanguage(), code, new LazySource(PythonUtils.createFakeSource()), false); + rootNode = PBytecodeRootNode.create(language, code, PythonUtils.createFakeSource(), false); if (code.isGeneratorOrCoroutine()) { - rootNode = new PBytecodeGeneratorFunctionRootNode(context.getLanguage(), rootNode.getFrameDescriptor(), (PBytecodeRootNode) rootNode, code.name); + rootNode = new PBytecodeGeneratorFunctionRootNode(language, rootNode.getFrameDescriptor(), (PBytecodeRootNode) rootNode, code.name); } } - return PythonUtils.getOrCreateCallTarget(rootNode); + return (PRootNode) rootNode; } @NeverDefault @@ -192,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) { @@ -210,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) { @@ -238,22 +270,4 @@ static Signature doCode(PCode code) { return code.getSignature(); } } - - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class GetCodeRootNode extends Node { - - public abstract RootNode execute(Node inliningTarget, PCode code); - - public static RootNode executeUncached(PCode code) { - return GetCodeRootNodeGen.getUncached().execute(null, code); - } - - @Specialization - static RootNode doIt(Node inliningTarget, PCode code, - @Cached GetCodeCallTargetNode getCodeCallTargetNode) { - return getCodeCallTargetNode.execute(inliningTarget, code).getRootNode(); - } - } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java index 7e4b524a9c..084f871177 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/PCode.java @@ -41,28 +41,20 @@ package com.oracle.graal.python.builtins.objects.code; import static com.oracle.graal.python.nodes.StringLiterals.J_EMPTY_STRING; -import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY; import static com.oracle.graal.python.util.PythonUtils.EMPTY_TRUFFLESTRING_ARRAY; import static com.oracle.graal.python.util.PythonUtils.toInternedTruffleStringUncached; import java.math.BigInteger; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.bytes.PBytes; -import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis; import com.oracle.graal.python.builtins.objects.function.Signature; import com.oracle.graal.python.builtins.objects.generator.PGenerator; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.CodeUnit; -import com.oracle.graal.python.compiler.OpCodes; import com.oracle.graal.python.nodes.PRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorFunctionRootNode; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorRootNode; @@ -71,14 +63,14 @@ import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.runtime.GilNode; -import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.BoolSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.DoubleSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.LongSequenceStorage; 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.RootCallTarget; import com.oracle.truffle.api.bytecode.BytecodeNode; @@ -109,7 +101,8 @@ public final class PCode extends PythonBuiltinObject { /* GraalPy-specific */ public static final int CO_GRAALPYHON_MODULE = 0x1000; - private final RootCallTarget callTarget; + private final RootNode rootNode; + @CompilationFinal private RootCallTarget callTarget; private final Signature signature; // number of local variables @@ -144,33 +137,7 @@ public final class PCode extends PythonBuiltinObject { // tuple of names of cell variables (referenced by containing scopes) private TruffleString[] cellvars; - public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget) { - super(cls, instanceShape); - this.callTarget = callTarget; - this.signature = Signature.fromCallTarget(callTarget); - } - - public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, int flags, int firstlineno, byte[] linetable, TruffleString filename) { - this(cls, instanceShape, callTarget); - this.flags = flags; - this.firstlineno = firstlineno; - this.linetable = linetable; - this.filename = filename; - } - - public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, Signature signature, BytecodeCodeUnit codeUnit) { - this(cls, instanceShape, callTarget, signature, codeUnit.varnames.length, -1, -1, null, null, - null, null, null, null, - codeUnit.name, codeUnit.qualname, -1, codeUnit.srcOffsetTable); - } - - public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, Signature signature, BytecodeDSLCodeUnit codeUnit) { - this(cls, instanceShape, callTarget, signature, codeUnit.varnames.length, -1, -1, null, null, - null, null, null, null, - codeUnit.name, codeUnit.qualname, -1, null); - } - - public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, Signature signature, int nlocals, + public PCode(Object cls, Shape instanceShape, RootNode rootNode, Signature signature, int nlocals, int stacksize, int flags, Object[] constants, TruffleString[] names, TruffleString[] varnames, TruffleString[] freevars, TruffleString[] cellvars, TruffleString filename, TruffleString name, TruffleString qualname, @@ -189,7 +156,7 @@ public PCode(Object cls, Shape instanceShape, RootCallTarget callTarget, Signatu this.linetable = linetable; this.freevars = freevars; this.cellvars = cellvars; - this.callTarget = callTarget; + this.rootNode = rootNode; this.signature = signature; assert signature != null; } @@ -212,39 +179,19 @@ private static TruffleString[] extractCellVars(RootNode rootNode) { } } - @TruffleBoundary - private static void setRootNodeFileName(RootNode rootNode, TruffleString filename) { - RootNode funcRootNode = rootNodeForExtraction(rootNode); - PythonContext.get(rootNode).setCodeFilename(funcRootNode.getCallTarget(), filename); - } - @TruffleBoundary public static TruffleString extractFileName(RootNode rootNode) { RootNode funcRootNode = rootNodeForExtraction(rootNode); - - PythonContext context = PythonContext.get(rootNode); - TruffleString filename; - if (context != null) { - if (rootNode instanceof PBytecodeRootNode) { - filename = context.getCodeUnitFilename(((PBytecodeRootNode) rootNode).getCodeUnit()); - } else { - filename = context.getCodeFilename(funcRootNode.getCallTarget()); - } - } else { - return toInternedTruffleStringUncached(funcRootNode.getName()); - } - if (filename != null) { - // for compiled modules, _imp._fix_co_filename will set the filename - return filename; + SourceSection sourceSection = funcRootNode.getSourceSection(); + if (sourceSection == null && funcRootNode instanceof PBytecodeDSLRootNode bytecodeDSLRootNode) { + bytecodeDSLRootNode.getRootNodes().ensureSourceInformation(); + sourceSection = funcRootNode.getSourceSection(); } - SourceSection src = funcRootNode.getSourceSection(); - String jFilename; - if (src != null) { - jFilename = getSourceSectionFileName(src); - } else { - jFilename = funcRootNode.getName(); + String fileName = getSourceSectionFileName(sourceSection); + if (fileName != null) { + return toInternedTruffleStringUncached(fileName); } - return toInternedTruffleStringUncached(jFilename); + return toInternedTruffleStringUncached(funcRootNode.getName()); } @TruffleBoundary @@ -293,7 +240,7 @@ private static int extractStackSize(RootNode rootNode) { BytecodeCodeUnit code = bytecodeRootNode.getCodeUnit(); return code.stacksize + code.varnames.length + code.cellvars.length + code.freevars.length; } - /** + /* * NB: This fallback case includes PBytecodeDSLRootNode. The Bytecode DSL stack does not * mirror a CPython stack (it's an operand stack for its own instruction set), so the frame * size is our best estimate. @@ -310,55 +257,23 @@ private static TruffleString[] extractVarnames(RootNode node) { return EMPTY_TRUFFLESTRING_ARRAY; } + private Object[] ensureConstants() { + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, constants == null)) { + CodeUnit codeUnit = getCodeUnit(getRootNode()); + constants = codeUnit != null ? new Object[codeUnit.constants.length] : PythonUtils.EMPTY_OBJECT_ARRAY; + } + return constants; + } + @TruffleBoundary - private static Object[] extractConstants(RootNode node) { - RootNode rootNode = rootNodeForExtraction(node); - if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { - if (rootNode instanceof PBytecodeDSLRootNode bytecodeDSLRootNode) { - BytecodeDSLCodeUnit co = bytecodeDSLRootNode.getCodeUnit(); - List constants = new ArrayList<>(); - for (int i = 0; i < co.constants.length; i++) { - Object constant = convertConstantToPythonSpace(rootNode, co.constants[i]); - constants.add(constant); - } - return constants.toArray(new Object[0]); - } - } else if (rootNode instanceof PBytecodeRootNode bytecodeRootNode) { - BytecodeCodeUnit co = bytecodeRootNode.getCodeUnit(); - Set bytecodeConstants = new HashSet<>(); - for (int bci = 0; bci < co.code.length;) { - OpCodes op = OpCodes.fromOpCode(co.code[bci]); - if (op.quickens != null) { - op = op.quickens; - } - if (op == OpCodes.LOAD_BYTE) { - bytecodeConstants.add(Byte.toUnsignedInt(co.code[bci + 1])); - } else if (op == OpCodes.LOAD_NONE) { - bytecodeConstants.add(PNone.NONE); - } else if (op == OpCodes.LOAD_TRUE) { - bytecodeConstants.add(true); - } else if (op == OpCodes.LOAD_FALSE) { - bytecodeConstants.add(false); - } else if (op == OpCodes.LOAD_ELLIPSIS) { - bytecodeConstants.add(PEllipsis.INSTANCE); - } else if (op == OpCodes.LOAD_INT || op == OpCodes.LOAD_LONG) { - bytecodeConstants.add(co.primitiveConstants[Byte.toUnsignedInt(co.code[bci + 1])]); - } else if (op == OpCodes.LOAD_DOUBLE) { - bytecodeConstants.add(Double.longBitsToDouble(co.primitiveConstants[Byte.toUnsignedInt(co.code[bci + 1])])); - } - bci += op.length(); - } - List constants = new ArrayList<>(); - for (int i = 0; i < co.constants.length; i++) { - Object constant = convertConstantToPythonSpace(rootNode, co.constants[i]); - if (constant != PNone.NONE || !bytecodeConstants.contains(PNone.NONE)) { - constants.add(constant); - } - } - constants.addAll(bytecodeConstants); - return constants.toArray(new Object[0]); + private Object getOrCreateConstant(int index) { + Object[] cachedConstants = ensureConstants(); + Object constant = cachedConstants[index]; + if (constant == null) { + constant = convertConstantToPythonSpace(index); + cachedConstants[index] = constant; } - return EMPTY_OBJECT_ARRAY; + return constant; } @TruffleBoundary @@ -370,7 +285,8 @@ private static TruffleString[] extractNames(RootNode node) { return EMPTY_TRUFFLESTRING_ARRAY; } - private static RootNode rootNodeForExtraction(RootNode rootNode) { + private static RootNode rootNodeForExtraction(RootNode rootNodeArg) { + RootNode rootNode = PythonLanguage.unwrapRootNode(rootNodeArg); if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { return PGenerator.unwrapContinuationRoot(rootNode); } else { @@ -406,8 +322,8 @@ private static CodeUnit getCodeUnit(RootNode node) { return null; } - RootNode getRootNode() { - return getRootCallTarget().getRootNode(); + public RootNode getRootNode() { + return rootNode; } RootNode getRootNodeForExtraction() { @@ -428,20 +344,17 @@ public TruffleString[] getCellVars() { return cellvars; } - public void setFilename(TruffleString filename) { - CompilerAsserts.neverPartOfCompilation(); - filename = PythonUtils.internString(filename); - - this.filename = filename; - RootNode rootNode = rootNodeForExtraction(getRootNode()); - setRootNodeFileName(rootNode, filename); - if (rootNode instanceof PBytecodeRootNode) { - PythonContext context = PythonContext.get(rootNode); - CodeUnit co = ((PBytecodeRootNode) rootNode).getCodeUnit(); - context.setCodeUnitFilename(co, filename); - for (int i = 0; i < co.constants.length; i++) { - if (co.constants[i] instanceof CodeUnit) { - context.setCodeUnitFilename((CodeUnit) co.constants[i], filename); + @TruffleBoundary + public void fixCoFilename(TruffleString filenameArg) { + filename = PythonUtils.internString(filenameArg); + /* + * New code objects inherit the filename from parent, so no need to eagerly construct them + * here + */ + if (constants != null) { + for (Object constant : constants) { + if (constant instanceof PCode code) { + code.filename = filename; } } } @@ -533,9 +446,9 @@ public TruffleString[] getVarnames() { } public byte[] getCodestring(Node node) { - RootNode rootNode = getRootNode(); - if (rootNode instanceof PRootNode) { - return ((PRootNode) rootNode).getCode(node); + RootNode rN = getRootNode(); + if (rN instanceof PRootNode) { + return ((PRootNode) rN).getCode(node); } else { return PythonUtils.EMPTY_BYTE_ARRAY; } @@ -546,24 +459,65 @@ public CodeUnit getCodeUnit() { } public Object[] getConstants() { - if (constants == null) { - constants = extractConstants(getRootNode()); + Object[] cachedConstants = ensureConstants(); + for (int i = 0; i < cachedConstants.length; i++) { + getOrCreateConstant(i); } - return constants; + return cachedConstants; + } + + public PCode getOrCreateChildCode(int index, BytecodeDSLCodeUnit codeUnit) { + Object[] cachedConstants = ensureConstants(); + PCode code = (PCode) cachedConstants[index]; + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, code == null)) { + code = createCode(codeUnit); + cachedConstants[index] = code; + } + return code; + } + + @TruffleBoundary + private PCode createCode(BytecodeDSLCodeUnit codeUnit) { + PBytecodeDSLRootNode outerRootNode = (PBytecodeDSLRootNode) getRootNodeForExtraction(); + PythonLanguage language = outerRootNode.getLanguage(); + PBytecodeDSLRootNode rN = language.createCachedRootNode(l -> codeUnit.createRootNode(l, outerRootNode.isInternal()), codeUnit); + return PFactory.createCode(language, rN, rN.getSignature(), codeUnit, getFilename()); + } + + public PCode getOrCreateChildCode(int index, BytecodeCodeUnit codeUnit) { + Object[] cachedConstants = ensureConstants(); + PCode code = (PCode) cachedConstants[index]; + if (code == null) { + code = createCode(codeUnit); + cachedConstants[index] = code; + } + return code; } @TruffleBoundary - private static Object convertConstantToPythonSpace(RootNode rootNode, Object o) { + private PCode createCode(BytecodeCodeUnit codeUnit) { + PBytecodeRootNode outerRootNode = (PBytecodeRootNode) getRootNodeForExtraction(); + PythonLanguage language = outerRootNode.getLanguage(); + PRootNode executableRootNode = language.createCachedRootNode( + l -> (PRootNode) PBytecodeRootNode.createMaybeGenerator(language, codeUnit, outerRootNode.getSource(), outerRootNode.isInternal()), codeUnit); + RootNode rN = executableRootNode; + if (executableRootNode instanceof PBytecodeGeneratorFunctionRootNode generatorRoot) { + rN = generatorRoot.getBytecodeRootNode(); + } + return PFactory.createCode(language, executableRootNode, ((PBytecodeRootNode) rN).getSignature(), codeUnit, getFilename()); + } + + @TruffleBoundary + private Object convertConstantToPythonSpace(int index) { + Object o = getCodeUnit().constants[index]; PythonLanguage language = PythonLanguage.get(null); if (o instanceof CodeUnit) { if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { BytecodeDSLCodeUnit code = (BytecodeDSLCodeUnit) o; - PBytecodeDSLRootNode root = code.createRootNode(PythonContext.get(rootNode), getSourceSection(rootNode).getSource()); - return PFactory.createCode(language, root.getCallTarget(), root.getSignature(), code); + return getOrCreateChildCode(index, code); } else { BytecodeCodeUnit code = (BytecodeCodeUnit) o; - PBytecodeRootNode bytecodeRootNode = PBytecodeRootNode.create(language, code, ((PBytecodeRootNode) rootNode).getLazySource(), rootNode.isInternal()); - return PFactory.createCode(language, bytecodeRootNode.getCallTarget(), bytecodeRootNode.getSignature(), code); + return getOrCreateChildCode(index, code); } } else if (o instanceof BigInteger) { return PFactory.createInt(language, (BigInteger) o); @@ -591,11 +545,6 @@ private static Object convertConstantToPythonSpace(RootNode rootNode, Object o) return o; } - @TruffleBoundary - private static SourceSection getSourceSection(RootNode rootNode) { - return rootNode.getSourceSection(); - } - public TruffleString[] getNames() { if (names == null) { names = extractNames(getRootNode()); @@ -636,7 +585,24 @@ public Signature getSignature() { } public RootCallTarget getRootCallTarget() { - return callTarget; + RootCallTarget ct = callTarget; + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, ct == null)) { + if (CompilerDirectives.inCompiledCode() && CompilerDirectives.isPartialEvaluationConstant(this)) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + } + ct = initializeCallTarget(); + } + return ct; + } + + @TruffleBoundary + private RootCallTarget initializeCallTarget() { + RootCallTarget ct = callTarget; + if (ct == null) { + ct = rootNode.getCallTarget(); + callTarget = ct; + } + return ct; } @ExportMessage @@ -687,15 +653,15 @@ public String toString() { @TruffleBoundary public String toDisassembledString(boolean quickened) { - RootNode rootNode = getRootCallTarget().getRootNode(); - if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER && rootNode instanceof PBytecodeDSLRootNode dslRoot) { + RootNode rN = getRootNode(); + if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER && rN instanceof PBytecodeDSLRootNode dslRoot) { return dslRoot.getCodeUnit().toString(quickened, dslRoot); - } else if (rootNode instanceof PBytecodeGeneratorRootNode r) { - rootNode = r.getBytecodeRootNode(); - } else if (rootNode instanceof PBytecodeGeneratorFunctionRootNode r) { - rootNode = r.getBytecodeRootNode(); + } else if (rN instanceof PBytecodeGeneratorRootNode r) { + rN = r.getBytecodeRootNode(); + } else if (rN instanceof PBytecodeGeneratorFunctionRootNode r) { + rN = r.getBytecodeRootNode(); } - if (rootNode instanceof PBytecodeRootNode bytecodeRootNode) { + if (rN instanceof PBytecodeRootNode bytecodeRootNode) { return bytecodeRootNode.getCodeUnit().toString(quickened, bytecodeRootNode); } return J_EMPTY_STRING; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java index 1c2d2fce81..872546b9dd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.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 @@ -108,12 +108,7 @@ public DynamicObject getStore() { } protected static Object[] keyArray(DynamicObjectStorage self) { - return DynamicObjectStorage.keyArray(self.store.getShape()); - } - - @TruffleBoundary - protected static Object[] keyArray(Shape shape) { - return shape.getKeyList().toArray(); + return DynamicObject.GetKeyArrayNode.getUncached().execute(self.store); } @GenerateUncached @@ -131,7 +126,7 @@ public abstract static class LengthNode extends Node { static int cachedLen(DynamicObjectStorage self, @SuppressWarnings("unused") @Cached("self.store.getShape()") Shape cachedShape, @Cached(value = "keyArray(self)", dimensions = 1) Object[] keys, - @Shared @Cached ReadAttributeFromPythonObjectNode readNode) { + @Shared @Cached(inline = false) ReadAttributeFromPythonObjectNode readNode) { int len = 0; for (Object key : keys) { len = incrementLen(self, readNode, len, key); @@ -139,11 +134,11 @@ static int cachedLen(DynamicObjectStorage self, return len; } - @Specialization(replaces = "cachedLen", guards = {"cachedShape == self.store.getShape()"}, limit = "3") - static int cachedKeys(DynamicObjectStorage self, - @SuppressWarnings("unused") @Cached("self.store.getShape()") Shape cachedShape, - @Cached(value = "keyArray(self)", dimensions = 1) Object[] keys, - @Shared @Cached ReadAttributeFromPythonObjectNode readNode) { + @Specialization(replaces = "cachedLen") + static int length(DynamicObjectStorage self, + @Shared @Cached(inline = false) ReadAttributeFromPythonObjectNode readNode, + @Cached DynamicObject.GetKeyArrayNode keyArrayNode) { + Object[] keys = keyArrayNode.execute(self.store); int len = 0; for (Object key : keys) { len = incrementLen(self, readNode, len, key); @@ -151,12 +146,6 @@ static int cachedKeys(DynamicObjectStorage self, return len; } - @Specialization(replaces = "cachedKeys") - static int length(DynamicObjectStorage self, - @Shared @Cached ReadAttributeFromPythonObjectNode readNode) { - return cachedKeys(self, self.store.getShape(), keyArray(self), readNode); - } - private static boolean hasStringKey(DynamicObjectStorage self, TruffleString key, ReadAttributeFromPythonObjectNode readNode) { return readNode.execute(self.store, key, PNone.NO_VALUE) != PNone.NO_VALUE; } @@ -186,19 +175,19 @@ abstract static class GetItemNode extends Node { @Specialization static Object string(Node inliningTarget, DynamicObjectStorage self, TruffleString key, @SuppressWarnings("unused") long keyHash, - @Shared("readKey") @Cached(inline = false) ReadAttributeFromPythonObjectNode readKey, - @Exclusive @Cached InlinedConditionProfile noValueProfile) { - Object result = readKey.execute(self.store, key, PNone.NO_VALUE); + @Shared @Cached ReadAttributeFromPythonObjectNode readKey, + @Shared @Cached InlinedConditionProfile noValueProfile) { + Object result = readKey.execute(inliningTarget, self.store, key, PNone.NO_VALUE); return noValueProfile.profile(inliningTarget, result == PNone.NO_VALUE) ? null : result; } - @Specialization(guards = "isBuiltinString.execute(inliningTarget, key)", limit = "1") + @Specialization(guards = "isBuiltinString.execute(inliningTarget, key)") @InliningCutoff static Object pstring(Node inliningTarget, DynamicObjectStorage self, PString key, @SuppressWarnings("unused") long keyHash, - @SuppressWarnings("unused") @Cached PyUnicodeCheckExactNode isBuiltinString, - @Cached CastToTruffleStringNode castStr, - @Shared("readKey") @Cached(inline = false) ReadAttributeFromPythonObjectNode readKey, - @Exclusive @Cached InlinedConditionProfile noValueProfile) { + @SuppressWarnings("unused") @Shared @Cached PyUnicodeCheckExactNode isBuiltinString, + @Cached(inline = false) CastToTruffleStringNode castStr, + @Shared @Cached ReadAttributeFromPythonObjectNode readKey, + @Shared @Cached InlinedConditionProfile noValueProfile) { return string(inliningTarget, self, castStr.execute(inliningTarget, key), -1, readKey, noValueProfile); } @@ -219,9 +208,9 @@ abstract static class GetItemNoStringKeyNode extends Node { @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN) static Object notString(Frame frame, DynamicObjectStorage self, Object key, long hashIn, @Bind Node inliningTarget, - @Shared("readKey") @Cached ReadAttributeFromPythonObjectNode readKey, + @Shared("readKey") @Cached(inline = false) ReadAttributeFromPythonObjectNode readKey, @Exclusive @Cached("self.store.getShape()") Shape cachedShape, - @Exclusive @Cached(value = "keyArray(cachedShape)", dimensions = 1) Object[] keyList, + @Exclusive @Cached(value = "keyArray(self)", dimensions = 1) Object[] keyList, @Shared("eqNode") @Cached PyObjectRichCompareBool eqNode, @Shared("hashNode") @Cached PyObjectHashNode hashNode, @Shared("noValueProfile") @Cached InlinedConditionProfile noValueProfile) { @@ -240,7 +229,7 @@ static Object notString(Frame frame, DynamicObjectStorage self, Object key, long @Specialization(replaces = "notString") static Object notStringLoop(Frame frame, DynamicObjectStorage self, Object key, long hashIn, @Bind Node inliningTarget, - @Shared("readKey") @Cached ReadAttributeFromPythonObjectNode readKey, + @Shared("readKey") @Cached(inline = false) ReadAttributeFromPythonObjectNode readKey, @Shared("eqNode") @Cached PyObjectRichCompareBool eqNode, @Shared("hashNode") @Cached PyObjectHashNode hashNode, @Shared("noValueProfile") @Cached InlinedConditionProfile noValueProfile) { @@ -280,7 +269,7 @@ void setStringKey(TruffleString key, Object value, DynamicObject.PutNode putNode } boolean shouldTransitionOnPut() { - // For now we do not use SIZE_THRESHOLD condition to transition storages that wrap + // For now, we do not use SIZE_THRESHOLD condition to transition storages that wrap // dictionaries retrieved via object's __dict__ boolean notDunderDict = store instanceof Store; int propertyCount = store.getShape().getPropertyCount(); @@ -303,7 +292,8 @@ static HashingStorage clearPlain(DynamicObjectStorage receiver, @Specialization(guards = "isPythonObject(receiver.getStore())") static HashingStorage clearObjectBacked(Node inliningTarget, DynamicObjectStorage receiver, - @Cached HiddenAttr.ReadNode readHiddenAttrNode) { + @Cached HiddenAttr.ReadNode readHiddenAttrNode, + @Cached DynamicObject.SetShapeFlagsNode setShapeFlagsNode) { /* * We cannot use resetShape as that would lose hidden keys, such as CLASS or OBJ_ID. * Construct a new storage instead and set it as the object's __dict__'s storage. @@ -312,6 +302,7 @@ static HashingStorage clearObjectBacked(Node inliningTarget, DynamicObjectStorag PythonObject owner = (PythonObject) receiver.getStore(); PDict dict = (PDict) readHiddenAttrNode.execute(inliningTarget, owner, HiddenAttr.DICT, null); if (dict != null && dict.getDictStorage() == receiver) { + setShapeFlagsNode.executeAdd(owner, PythonObject.HAS_DICT | PythonObject.HAS_MATERIALIZED_DICT); dict.setDictStorage(newStorage); } return newStorage; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/EconomicMapStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/EconomicMapStorage.java index 92c5d0a8a6..75f09f299f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/EconomicMapStorage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/EconomicMapStorage.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 @@ -66,8 +66,7 @@ import com.oracle.truffle.api.strings.TruffleString; import com.oracle.truffle.api.strings.TruffleString.HashCodeNode; -public class EconomicMapStorage extends HashingStorage { - +public class EconomicMapStorage extends ObjectHashMap { public static EconomicMapStorage create() { return new EconomicMapStorage(); } @@ -80,18 +79,16 @@ public static EconomicMapStorage create(int initialCapacity) { return new EconomicMapStorage(initialCapacity); } - final ObjectHashMap map; - private EconomicMapStorage(int initialCapacity) { - this.map = new ObjectHashMap(initialCapacity); + super(initialCapacity); } private EconomicMapStorage() { this(4); } - public EconomicMapStorage(ObjectHashMap original, boolean copy) { - this.map = copy ? original.copy() : original; + private EconomicMapStorage(EconomicMapStorage original) { + super(original); } @TruffleBoundary @@ -109,7 +106,7 @@ public static EconomicMapStorage createGeneric(LinkedHashMap map } public int length() { - return map.size(); + return size(); } static boolean advance(MapCursor cursor) { @@ -128,51 +125,43 @@ static Object getValue(MapCursor cursor) { return cursor.getValue(); } - void clear() { - map.clear(); - } - - public boolean mapIsEqualTo(ObjectHashMap other) { - return other == this.map; - } - public HashingStorage copy() { - return new EconomicMapStorage(this.map, true); + return new EconomicMapStorage(this); } protected void setValueForAllKeys(VirtualFrame frame, Node inliningTarget, Object value, ObjectHashMap.PutNode putNode, InlinedLoopConditionProfile loopProfile) { - MapCursor cursor = map.getEntries(); - final int size = map.size(); + MapCursor cursor = getEntries(); + final int size = size(); loopProfile.profileCounted(inliningTarget, size); LoopNode.reportLoopCount(putNode, size); while (loopProfile.inject(inliningTarget, advance(cursor))) { - putNode.put(frame, inliningTarget, map, getDictKey(cursor), value); + putNode.put(frame, inliningTarget, this, getDictKey(cursor), value); } } @TruffleBoundary public Object removeUncached(Object key, long hash) { - return RemoveNode.removeUncached(map, key, hash); + return RemoveNode.removeUncached(this, key, hash); } @TruffleBoundary public void putUncached(TruffleString key, Object value) { - PutNode.putUncached(map, key, PyObjectHashNode.hash(key, HashCodeNode.getUncached()), value); + PutNode.putUncached(this, key, PyObjectHashNode.hash(key, HashCodeNode.getUncached()), value); } @TruffleBoundary public void putUncached(Object key, Object value) { - PutNode.putUncached(map, key, PyObjectHashNode.executeUncached(key), value); + PutNode.putUncached(this, key, PyObjectHashNode.executeUncached(key), value); } @TruffleBoundary public void putUncached(Object key, long hash, Object value) { - PutNode.putUncached(map, key, hash, value); + PutNode.putUncached(this, key, hash, value); } @TruffleBoundary public void putUncached(int key, Object value) { - PutNode.putUncached(map, key, PyObjectHashNode.hash(key), value); + PutNode.putUncached(this, key, PyObjectHashNode.hash(key), value); } @TruffleBoundary @@ -201,7 +190,7 @@ public String toString() { StringBuilder builder = new StringBuilder(); builder.append("map(size=").append(length()).append(", {"); String sep = ""; - MapCursor cursor = map.getEntries(); + MapCursor cursor = getEntries(); int i = 0; while (advance(cursor)) { i++; @@ -225,7 +214,7 @@ public abstract static class EconomicMapSetStringKey extends SpecializedSetStrin static void doIt(Node inliningTarget, HashingStorage self, TruffleString key, Object value, @Cached PyObjectHashNode hashNode, @Cached ObjectHashMap.PutNode putNode) { - putNode.put(null, inliningTarget, ((EconomicMapStorage) self).map, key, hashNode.execute(null, inliningTarget, key), value); + putNode.put(null, inliningTarget, (EconomicMapStorage) self, key, hashNode.execute(null, inliningTarget, key), value); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingCollectionNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingCollectionNodes.java index 0102c81b7b..636edde508 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingCollectionNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingCollectionNodes.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 @@ -319,4 +319,39 @@ static HashingStorage doGeneric(VirtualFrame frame, Node inliningTarget, Object return getHashingStorageNode.getForSets(frame, inliningTarget, other); } } + + /** + * CPython's set symmetric_difference_update uses stored hashes for exact sets and dicts, but + * first materializes other iterables, including dict views, as a temporary set. + */ + @GenerateInline(inlineByDefault = true) + public abstract static class GetSetStorageForXorNode extends PNodeWithContext { + + public abstract HashingStorage execute(VirtualFrame frame, Node inliningTarget, Object iterator); + + @Specialization + static HashingStorage doHashingCollection(PHashingCollection other) { + return other.getDictStorage(); + } + + @Specialization(guards = "!isPHashingCollection(other)") + @InliningCutoff + static HashingStorage doGeneric(VirtualFrame frame, Node inliningTarget, Object other, + @Cached PyObjectGetIter getIter, + @Cached PyIterNextNode nextNode, + @Exclusive @Cached HashingStorageSetItem setStorageItem) { + HashingStorage curStorage = EmptyStorage.INSTANCE; + Object iterator = getIter.execute(frame, inliningTarget, other); + while (true) { + Object key; + try { + key = nextNode.execute(frame, inliningTarget, iterator); + } catch (IteratorExhausted e) { + return curStorage; + } + curStorage = setStorageItem.execute(frame, inliningTarget, curStorage, key, PNone.NONE); + } + } + } + } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorage.java index 51e3cd6ef5..648fbf5da3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorage.java @@ -75,6 +75,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.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; @@ -190,6 +191,7 @@ public static HashingStorage addKeyValuesToStorage(VirtualFrame frame, HashingSt // partial impl dict_update_arg @GenerateCached @GenerateInline(false) + @GenerateUncached public abstract static class ObjectToArrayPairNode extends PNodeWithContext { public abstract ArrayBuilder execute(VirtualFrame frame, Object mapping, Object keyAttr); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java index cfb38025d2..564d8a7b89 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java @@ -63,12 +63,14 @@ import com.oracle.graal.python.builtins.objects.common.HashingStorageNodesFactory.HashingStorageSetItemWithHashNodeGen; import com.oracle.graal.python.builtins.objects.common.KeywordsStorage.GetKeywordsStorageItemNode; import com.oracle.graal.python.builtins.objects.common.ObjectHashMap.PutNode; +import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.lib.PyObjectRichCompareBool; import com.oracle.graal.python.lib.PyUnicodeCheckExactNode; 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.attributes.ReadAttributeFromPythonObjectNode; import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.nodes.util.CastBuiltinStringToTruffleStringNode; @@ -117,7 +119,7 @@ public static Object getItemWithHash(HashingStorage self, Object key, long keyHa @Specialization static Object economicMap(Frame frame, Node inliningTarget, EconomicMapStorage self, Object key, long keyHash, @Cached ObjectHashMap.GetNode getNode) { - return getNode.execute(frame, inliningTarget, self.map, key, keyHash); + return getNode.execute(frame, inliningTarget, self, key, keyHash); } @Specialization @@ -146,6 +148,46 @@ static Object foreign(Node inliningTarget, ForeignHashingStorage self, Object ke } } + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class HashingStorageGetItemStringKey extends Node { + public abstract Object execute(Node inliningTarget, HashingStorage self, TruffleString key); + + @Specialization + static Object economicMap(Node inliningTarget, EconomicMapStorage self, TruffleString key, + @Cached TruffleString.HashCodeNode hashCodeNode, + @Cached ObjectHashMap.GetNode getNode) { + return getNode.execute(null, inliningTarget, self, key, PyObjectHashNode.hash(key, hashCodeNode)); + } + + @Specialization + static Object dom(Node inliningTarget, DynamicObjectStorage self, TruffleString key, + @Cached ReadAttributeFromPythonObjectNode readKey, + @Cached InlinedConditionProfile noValueProfile) { + return DynamicObjectStorage.GetItemNode.string(inliningTarget, self, key, -1, readKey, noValueProfile); + } + + @Specialization + @SuppressWarnings("unused") + static Object empty(EmptyStorage self, TruffleString key) { + return null; + } + + @Specialization + @InliningCutoff + static Object keywords(Node inliningTarget, KeywordsStorage self, TruffleString key, + @Cached GetKeywordsStorageItemNode getNode) { + return getNode.execute(null, inliningTarget, self, key, -1); + } + + @Specialization + static Object foreign(Node inliningTarget, ForeignHashingStorage self, TruffleString key, + @Cached ForeignHashingStorage.GetNode getNode) { + return getNode.execute(inliningTarget, self, key); + } + } + @GenerateUncached @GenerateInline @GenerateCached(false) @@ -178,13 +220,13 @@ public final Object execute(Node inliningTarget, HashingStorage self, TruffleStr @Specialization(guards = "isEconomicMapOrEmpty(self)") static Object economicMap(Frame frame, Node inliningTarget, HashingStorage self, Object key, - @Cached PyObjectHashNode hashNode, + @Exclusive @Cached PyObjectHashNode hashNode, @Cached InlinedConditionProfile isEconomicMapProfile, @Cached ObjectHashMap.GetNode getNode) { // We must not omit the potentially side-effecting call to __hash__ long hash = hashNode.execute(frame, inliningTarget, key); if (isEconomicMapProfile.profile(inliningTarget, self instanceof EconomicMapStorage)) { - return getNode.execute(frame, inliningTarget, ((EconomicMapStorage) self).map, key, hash); + return getNode.execute(frame, inliningTarget, (EconomicMapStorage) self, key, hash); } else { return null; } @@ -231,12 +273,16 @@ abstract static class SpecializedSetStringKey extends Node { } static EconomicMapStorage dynamicObjectStorageToEconomicMap(Node inliningTarget, DynamicObjectStorage s, + DynamicObject.SetShapeFlagsNode setShapeFlags, DynamicObject.GetKeyArrayNode getKeyArrayNode, DynamicObject.GetNode getNode, PyObjectHashNode hashNode, ObjectHashMap.PutNode putNode) { DynamicObject store = s.store; + if (store instanceof PythonObject pyObj) { + setShapeFlags.executeAdd(pyObj, PythonObject.HAS_MATERIALIZED_DICT); + } Object[] keys = getKeyArrayNode.execute(store); EconomicMapStorage result = EconomicMapStorage.create(keys.length); - ObjectHashMap resultMap = result.map; + ObjectHashMap resultMap = result; for (Object k : keys) { if (k instanceof TruffleString) { Object v = getNode.execute(store, k, PNone.NO_VALUE); @@ -267,16 +313,16 @@ public final HashingStorage executeCached(Frame frame, HashingStorage self, Obje @Specialization static HashingStorage economicMap(Frame frame, Node inliningTarget, EconomicMapStorage self, Object key, long keyHash, Object value, @Exclusive @Cached PutNode putNode) { - putNode.execute(frame, inliningTarget, self.map, key, keyHash, value); + putNode.execute(frame, inliningTarget, self, key, keyHash, value); return self; } @Specialization static HashingStorage empty(Frame frame, Node inliningTarget, @SuppressWarnings("unused") EmptyStorage self, Object key, long keyHash, Object value, @Exclusive @Cached PutNode putNode) { - EconomicMapStorage storage = EconomicMapStorage.create(1); - putNode.execute(frame, inliningTarget, storage.map, key, keyHash, value); - return storage; + EconomicMapStorage result = EconomicMapStorage.create(1); + putNode.execute(frame, inliningTarget, result, key, keyHash, value); + return result; } @Specialization(guards = "!self.shouldTransitionOnPut()") @@ -341,10 +387,11 @@ static HashingStorage domTransition(Frame frame, Node inliningTarget, DynamicObj @Cached PyObjectHashNode hashNode, @Cached ObjectHashMap.PutNode putUnsafeNode, @Cached PutNode putNode, + @Cached DynamicObject.SetShapeFlagsNode setShapeFlags, @Cached DynamicObject.GetKeyArrayNode getKeyArrayNode, @Cached DynamicObject.GetNode getNode) { - EconomicMapStorage result = dynamicObjectStorageToEconomicMap(inliningTarget, self, getKeyArrayNode, getNode, hashNode, putUnsafeNode); - putNode.execute(frame, inliningTarget, result.map, key, keyHash, value); + EconomicMapStorage result = dynamicObjectStorageToEconomicMap(inliningTarget, self, setShapeFlags, getKeyArrayNode, getNode, hashNode, putUnsafeNode); + putNode.execute(frame, inliningTarget, result, key, keyHash, value); return result; } } @@ -386,7 +433,7 @@ public final HashingStorage execute(Node inliningTarget, HashingStorage self, Tr static HashingStorage economicMap(Frame frame, Node inliningTarget, EconomicMapStorage self, Object key, Object value, @Exclusive @Cached PyObjectHashNode hashNode, @Exclusive @Cached PutNode putNode) { - putNode.execute(frame, inliningTarget, self.map, key, hashNode.execute(frame, inliningTarget, key), value); + putNode.execute(frame, inliningTarget, self, key, hashNode.execute(frame, inliningTarget, key), value); return self; } @@ -394,10 +441,11 @@ static HashingStorage economicMap(Frame frame, Node inliningTarget, EconomicMapS static HashingStorage empty(Frame frame, Node inliningTarget, @SuppressWarnings("unused") EmptyStorage self, Object key, Object value, @Exclusive @Cached PyObjectHashNode hashNode, @Exclusive @Cached PutNode putNode) { - // The ObjectHashMap.PutNode is @Exclusive because profiles for a put into a freshly new - // allocated map can be quite different to profiles in the other situations when we are - // putting into a map that already has or will have some more items in it - // It is also @Cached(inline = false) because inlining it triggers GR-44836 + // Route the first insertion through the EconomicMapStorage specialization so the + // EmptyStorage fast path keeps sharing the regular EconomicMapStorage setup logic. + // The ObjectHashMap.PutNode is @Exclusive because profiles for a put into a freshly + // allocated map can differ from puts into a map that already has or will soon have + // more entries. // TODO: do we want to try DynamicObjectStorage if the key is a string? return economicMap(frame, inliningTarget, EconomicMapStorage.create(1), key, value, hashNode, putNode); } @@ -469,10 +517,11 @@ static HashingStorage domTransition(Frame frame, Node inliningTarget, DynamicObj @Cached PyObjectHashNode hashNode, @Cached ObjectHashMap.PutNode putUnsafeNode, @Cached PutNode putNode, + @Cached DynamicObject.SetShapeFlagsNode setShapeFlags, @Cached DynamicObject.GetKeyArrayNode getKeyArrayNode, @Cached DynamicObject.GetNode getNode) { - EconomicMapStorage result = dynamicObjectStorageToEconomicMap(inliningTarget, self, getKeyArrayNode, getNode, hashNode, putUnsafeNode); - putNode.execute(frame, inliningTarget, result.map, key, hashNode.execute(frame, inliningTarget, key), value); + EconomicMapStorage result = dynamicObjectStorageToEconomicMap(inliningTarget, self, setShapeFlags, getKeyArrayNode, getNode, hashNode, putUnsafeNode); + putNode.execute(frame, inliningTarget, result, key, hashNode.execute(frame, inliningTarget, key), value); return result; } } @@ -488,7 +537,7 @@ public static boolean executeUncached(HashingStorage self, Object key, Object to } public static void executeUncachedWithHash(EconomicMapStorage storage, Object key, long hash) { - ObjectHashMapFactory.RemoveNodeGen.getUncached().execute(null, null, storage.map, key, hash); + ObjectHashMapFactory.RemoveNodeGen.getUncached().execute(null, null, storage, key, hash); } public final boolean execute(Node inliningTarget, HashingStorage self, TruffleString key, Object toUpdate) { @@ -530,7 +579,7 @@ static Object economicMap(Frame frame, Node inliningTarget, HashingStorage self, long hash = hashNode.execute(frame, inliningTarget, key); if (self instanceof EconomicMapStorage economicMap) { isEconomicMapProfile.enter(inliningTarget); - Object result = removeNode.execute(frame, inliningTarget, economicMap.map, key, hash); + Object result = removeNode.execute(frame, inliningTarget, economicMap, key, hash); return needsValue ? result : result != null; } return needsValue ? null : false; @@ -573,7 +622,7 @@ static Object keywords(Frame frame, Node inliningTarget, KeywordsStorage self, O EconomicMapStorage newStorage = EconomicMapStorage.create(self.length()); self.addAllTo(inliningTarget, newStorage, specializedPutNode); toUpdate.setDictStorage(newStorage); - Object result = removeNode.execute(frame, inliningTarget, newStorage.map, key, hashNode.execute(frame, inliningTarget, key)); + Object result = removeNode.execute(frame, inliningTarget, newStorage, key, hashNode.execute(frame, inliningTarget, key)); return needsValue ? result : result != null; } @@ -842,7 +891,7 @@ public final HashingStorageIterator execute(Node node, HashingStorage storage) { @Specialization static HashingStorageIterator economicMap(@SuppressWarnings("unused") EconomicMapStorage self) { HashingStorageIterator it = new HashingStorageIterator(true); - it.index = self.map.usedHashes; + it.index = self.usedHashes; return it; } @@ -898,7 +947,7 @@ public static HashingStorageIteratorNext create() { @Specialization(guards = "!it.isReverse") static boolean economicMap(EconomicMapStorage self, HashingStorageIterator it) { - ObjectHashMap map = self.map; + ObjectHashMap map = self; it.index++; while (it.index < map.usedHashes) { Object val = map.getValue(it.index); @@ -914,7 +963,7 @@ static boolean economicMap(EconomicMapStorage self, HashingStorageIterator it) { @Specialization(guards = "it.isReverse") static boolean economicMapReverse(EconomicMapStorage self, HashingStorageIterator it) { - ObjectHashMap map = self.map; + ObjectHashMap map = self; it.index--; while (it.index >= 0) { Object val = map.getValue(it.index); @@ -1078,7 +1127,7 @@ public static HashingStorageIteratorKey create() { @Specialization static Object economicMap(EconomicMapStorage self, HashingStorageIterator it) { - return self.map.getKey(it.index); + return self.getKey(it.index); } @Specialization @@ -1123,7 +1172,7 @@ public static long executeUncached(HashingStorage storage, HashingStorageIterato @Specialization static long economicMap(EconomicMapStorage self, HashingStorageIterator it) { - return self.map.hashes[it.index]; + return self.getHash(it.index); } @Specialization @@ -1171,7 +1220,7 @@ public abstract static class HashingStoragePop extends Node { @Specialization static Object[] economicMap(Node inliningTarget, EconomicMapStorage self, @SuppressWarnings("unused") Object toUpdate, @Cached ObjectHashMap.PopNode popNode) { - return popNode.execute(inliningTarget, self.map); + return popNode.execute(inliningTarget, self); } // Other storages should not have any side effects, it's OK if they call __eq__ @@ -1321,23 +1370,23 @@ public ResultAndOther(ObjectHashMap result, HashingStorage other) { @GenerateInline @GenerateCached(false) @ImportStatic({PGuards.class}) - public abstract static class HashingStorageXorCallback extends HashingStorageForEachCallback { + public abstract static class HashingStorageXorCallback extends HashingStorageForEachCallback { @Override - public abstract ResultAndOther execute(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, ResultAndOther accumulator); + public abstract EconomicMapStorage execute(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, EconomicMapStorage accumulator); @Specialization - static ResultAndOther doGeneric(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, ResultAndOther acc, + static EconomicMapStorage doGeneric(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, EconomicMapStorage acc, @Cached PutNode putResultNode, - @Cached HashingStorageGetItemWithHash getFromOther, + @Cached ObjectHashMap.RemoveNode removeResultNode, @Cached HashingStorageIteratorKey iterKey, @Cached HashingStorageIteratorValue iterValue, @Cached HashingStorageIteratorKeyHash iterHash) { Object key = iterKey.execute(inliningTarget, storage, it); long hash = iterHash.execute(frame, inliningTarget, storage, it); - Object otherValue = getFromOther.execute(frame, inliningTarget, acc.other, key, hash); - if (otherValue == null) { - putResultNode.put(frame, inliningTarget, acc.result, key, hash, iterValue.execute(inliningTarget, storage, it)); + Object removedValue = removeResultNode.execute(frame, inliningTarget, acc, key, hash); + if (removedValue == null) { + putResultNode.put(frame, inliningTarget, acc, key, hash, iterValue.execute(inliningTarget, storage, it)); } return acc; } @@ -1348,22 +1397,41 @@ static ResultAndOther doGeneric(Frame frame, Node inliningTarget, HashingStorage @GenerateCached(false) @ImportStatic({PGuards.class}) public abstract static class HashingStorageXor extends Node { - public abstract HashingStorage execute(Frame frame, Node inliningTarget, HashingStorage a, HashingStorage b); + abstract HashingStorage execute(Frame frame, Node inliningTarget, HashingStorage left, HashingStorage right, boolean leftMayBeMutated); + + public final HashingStorage executePreservingLeft(Frame frame, Node inliningTarget, HashingStorage left, HashingStorage right) { + return execute(frame, inliningTarget, left, right, false); + } + + public final HashingStorage executeMutatingLeft(Frame frame, Node inliningTarget, HashingStorage left, HashingStorage right) { + return execute(frame, inliningTarget, left, right, true); + } @Specialization - static HashingStorage doIt(Frame frame, Node inliningTarget, HashingStorage aStorage, HashingStorage bStorage, - @Cached HashingStorageForEach forEachA, - @Cached HashingStorageForEach forEachB, - @Cached HashingStorageXorCallback callbackA, - @Cached HashingStorageXorCallback callbackB) { - final EconomicMapStorage result = EconomicMapStorage.createWithSideEffects(); - ObjectHashMap resultMap = result.map; + static HashingStorage doEconomicLeft(Frame frame, Node inliningTarget, EconomicMapStorage leftStorage, HashingStorage rightStorage, + boolean leftMayBeMutated, + @Exclusive @Cached HashingStorageForEach forEachRight, + @Exclusive @Cached HashingStorageXorCallback callback) { + if (leftStorage == rightStorage) { + return EmptyStorage.INSTANCE; + } + EconomicMapStorage result = leftMayBeMutated ? leftStorage : (EconomicMapStorage) leftStorage.copy(); + forEachRight.execute(frame, inliningTarget, rightStorage, callback, result); + return result; + } - ResultAndOther accA = new ResultAndOther(resultMap, bStorage); - forEachA.execute(frame, inliningTarget, aStorage, callbackA, accA); + @Specialization(replaces = "doEconomicLeft") + static HashingStorage doIt(Frame frame, Node inliningTarget, HashingStorage leftStorage, HashingStorage rightStorage, + @SuppressWarnings("unused") boolean leftMayBeMutated, + @Exclusive @Cached HashingStorageForEach forEachLeft, + @Exclusive @Cached HashingStorageForEach forEachRight, + @Exclusive @Cached HashingStorageTransferItem transferItem, + @Exclusive @Cached HashingStorageXorCallback callback) { + final EconomicMapStorage result = EconomicMapStorage.createWithSideEffects(); - ResultAndOther accB = new ResultAndOther(resultMap, aStorage); - forEachB.execute(frame, inliningTarget, bStorage, callbackB, accB); + HashingStorage copiedLeft = forEachLeft.execute(frame, inliningTarget, leftStorage, transferItem, result); + assert copiedLeft == result; + forEachRight.execute(frame, inliningTarget, rightStorage, callback, result); return result; } @@ -1409,7 +1477,7 @@ static HashingStorage doIt(Frame frame, Node inliningTarget, HashingStorage aSto @Cached HashingStorageForEach forEachA, @Cached HashingStorageIntersectCallback callback) { final EconomicMapStorage result = EconomicMapStorage.createWithSideEffects(); - ResultAndOther acc = new ResultAndOther(result.map, bStorage); + ResultAndOther acc = new ResultAndOther(result, bStorage); forEachA.execute(frame, inliningTarget, aStorage, callback, acc); return result; } @@ -1456,7 +1524,7 @@ static HashingStorage doIt(Frame frame, Node inliningTarget, HashingStorage aSto @Cached HashingStorageForEach forEachA, @Cached HashingStorageDiffCallback callback) { final EconomicMapStorage result = EconomicMapStorage.createWithSideEffects(); - ResultAndOther acc = new ResultAndOther(result.map, bStorage); + ResultAndOther acc = new ResultAndOther(result, bStorage); forEachA.execute(frame, inliningTarget, aStorage, callback, acc); return result; } @@ -1586,20 +1654,20 @@ public abstract static class HashingStorageTransferItem extends HashingStorageFo @Specialization static EconomicMapStorage economic2Economic(Frame frame, Node inliningTarget, EconomicMapStorage src, HashingStorageIterator it, EconomicMapStorage destStorage, - @Cached PutNode putNode) { - ObjectHashMap srcMap = src.map; - putNode.put(frame, inliningTarget, destStorage.map, srcMap.getKey(it.index), srcMap.hashes[it.index], srcMap.getValue(it.index)); + @Exclusive @Cached PutNode putNode) { + ObjectHashMap srcMap = src; + putNode.put(frame, inliningTarget, destStorage, srcMap.getKey(it.index), srcMap.getHash(it.index), srcMap.getValue(it.index)); return destStorage; } @Specialization(replaces = "economic2Economic") @InliningCutoff static HashingStorage economic2Generic(Frame frame, Node inliningTarget, EconomicMapStorage src, HashingStorageIterator it, HashingStorage destStorage, - @Cached HashingStorageSetItemWithHash setItemWithHash) { + @Exclusive @Cached HashingStorageSetItemWithHash setItemWithHash) { // Note that the point is to avoid side-effecting __hash__ call. Since the source is // economic map, the key may be an arbitrary object. - ObjectHashMap srcMap = src.map; - return setItemWithHash.execute(frame, inliningTarget, destStorage, srcMap.getKey(it.index), srcMap.hashes[it.index], srcMap.getValue(it.index)); + ObjectHashMap srcMap = src; + return setItemWithHash.execute(frame, inliningTarget, destStorage, srcMap.getKey(it.index), srcMap.getHash(it.index), srcMap.getValue(it.index)); } @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/ObjectHashMap.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/ObjectHashMap.java index 431598c1ed..182211f628 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/ObjectHashMap.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/ObjectHashMap.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,8 +42,6 @@ import static com.oracle.truffle.api.CompilerDirectives.SLOWPATH_PROBABILITY; -import java.util.Arrays; - import com.oracle.graal.python.builtins.objects.common.ObjectHashMapFactory.PutNodeGen; import com.oracle.graal.python.builtins.objects.common.ObjectHashMapFactory.RemoveNodeGen; import com.oracle.graal.python.lib.PyObjectRichCompareBool; @@ -52,10 +50,12 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.TruffleSafepoint; +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.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.nodes.LoopNode; @@ -109,14 +109,10 @@ *

    * Areas for future improvements: *

      - *
    • Use byte[] array for the sparse indices array and determine the size of an index according to - * the compact array size, i.e., 1 byte is enough for 256 items. This needs to also properly handle - * the bit flag used to mark collisions.
    • *
    • Use another bit from the index in the sparse indices array to remember index of removed * items, i.e., dummy items would carry the old index and collision mask. Such dummy items can be * reused when inserting new items. This will help with the insert/remove of the same key * scenario.
    • - *
    • Inline {@link ObjectHashMap} into {@code EconomicMapStorage} to save an indirection.
    • *
    • New strategy for long keys where the hashes array is used to store the keys, and the * keysAndValues array will store just values. Can be implemented by extending this class and * overriding few methods.
    • @@ -124,12 +120,16 @@ * {@code None} and there is no need to allocate space for values in the keysAndValues array. *
    */ -public final class ObjectHashMap { +public class ObjectHashMap extends HashingStorage { + private static final int TIGHT_ENTRY_CAPACITY_LIMIT = 8; + + static final int INDEX_BYTE_SIZE_CACHE_LIMIT = 3; + /** * Every hash map will preallocate at least this many buckets (and corresponding # of slots for * the real items). */ - private static final int INITIAL_INDICES_SIZE = 8; + private static final int INITIAL_INDICES_SIZE = 4; /** * We limit the max size of preallocated hash maps. See the comment in the ctor. @@ -137,21 +137,32 @@ public final class ObjectHashMap { private static final int MAX_PREALLOCATED_INDICES_SIZE = 1 << 20; /** - * Indices that participate in a collision chain are marked with the sign bit. + * Indices that participate in a collision chain are marked with the sign bit in the logical + * representation. The physical storage may use a narrower primitive array. */ private static final int COLLISION_MASK = 1 << 31; + private static final int BYTE_COLLISION_MASK = 1 << 7; + private static final int SHORT_COLLISION_MASK = 1 << 15; + private static final int BYTE_COLLISION_SHIFT = 24; + private static final int SHORT_COLLISION_SHIFT = 16; + private static final int INT_COLLISION_SHIFT = 0; /** - * We need some placeholders. Those masked with {@link #COLLISION_MASK} give numbers higher than - * our max number of items, which is MAX_INT/2, because we cram they keys and values together - * into one array. + * Sparse table indices use 0 and 1 as reserved markers and store real compact-array indices as + * {@code index + INDEX_OFFSET}. The collision bit is kept separately. */ - private static final int DUMMY_INDEX = -2; - private static final int EMPTY_INDEX = -1; - - private static void markCollision(int[] indices, int compactIndex) { - assert indices[compactIndex] != EMPTY_INDEX; - indices[compactIndex] = indices[compactIndex] | COLLISION_MASK; + private static final int EMPTY_INDEX = 0; + private static final int DUMMY_INDEX = 1; + private static final int INDEX_OFFSET = 2; + private static final int MAX_BYTE_INDEX = (BYTE_COLLISION_MASK - 1) - INDEX_OFFSET; + private static final int MAX_SHORT_INDEX = (SHORT_COLLISION_MASK - 1) - INDEX_OFFSET; + + private static void markCollision(byte[] metadata, int indicesOffset, int indexByteSize, int physicalCollisionMask, int compactIndex) { + int index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); + assert index != EMPTY_INDEX; + if (index != DUMMY_INDEX) { + setIndex(metadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex, index | COLLISION_MASK); + } } private static boolean isCollision(int index) { @@ -159,25 +170,17 @@ private static boolean isCollision(int index) { } private static int unwrapIndex(int value) { - return value & ~COLLISION_MASK; + return (value & ~COLLISION_MASK) - INDEX_OFFSET; } - /** - * This is the factor how much the map grows when new entries are added. Note that we grow - * according to the used slots for real items, not according to the buckets count, because when - * "growing" we also remove dummy entries, so "growing" could mean that we also shrink. - */ - private static final int GROWTH_RATE = 4; - private static final long PERTURB_SHIFT = 5; // It takes at most this many >>> shifts to turn any long into 0 private static final int PERTURB_SHIFTS_COUT = 13; - // Sparse array with indices pointing to hashes and keysAndValues - private int[] indices; + // Packed metadata: hashes first, then sparse indices. + private byte[] metadata; - // Compact arrays with the actual dict items: - long[] hashes; + // Compact array with the actual dict items: Object[] keysAndValues; // How many real items are in the dict @@ -194,12 +197,12 @@ private static int unwrapIndex(int value) { public ObjectHashMap(int capacity) { int allocateSize; - if (capacity <= INITIAL_INDICES_SIZE) { + int entryCapacity; + if (capacity <= 0) { allocateSize = INITIAL_INDICES_SIZE; + entryCapacity = getUsableSize(allocateSize); } else { - // We need the hash table of this size, in order to accommodate "capacity" many entries - int indicesCapacity = capacity + (capacity / 3); - if (indicesCapacity < 0 || indicesCapacity > MAX_PREALLOCATED_INDICES_SIZE) { + if (capacity > getUsableSize(MAX_PREALLOCATED_INDICES_SIZE)) { // This oddity is here because in some cases we are asked to allocate very large // dict in a situation where CPython (probably) does not preallocate at all and // fails later during the actual insertion on something unrelated before it can @@ -207,13 +210,13 @@ public ObjectHashMap(int capacity) { // behavior, so we take it easy if the requested size is too large. Maybe we should // rather revisit all such callsites instead of fixing this here... allocateSize = MAX_PREALLOCATED_INDICES_SIZE; + entryCapacity = getUsableSize(allocateSize); } else { - int pow2 = getNextPow2(indicesCapacity); - assert pow2 > INITIAL_INDICES_SIZE; - allocateSize = pow2; + allocateSize = getMinBucketsCount(capacity); + entryCapacity = getRequestedEntryCapacity(capacity, allocateSize); } } - allocateData(allocateSize); + allocateData(allocateSize, entryCapacity); } public ObjectHashMap() { @@ -222,16 +225,24 @@ public ObjectHashMap() { allocateData(INITIAL_INDICES_SIZE); } - private void allocateData(int newSize) { - assert isPow2(newSize); - indices = new int[newSize]; - Arrays.fill(indices, EMPTY_INDEX); - // since we allow ourselves to fill only up to 3/4 of the hash table, we need this many - // entries for the actual values: (we intentionally over-allocate by a small constant) - int quarter = newSize >> 2; - int usableSize = 3 * quarter + 2; - hashes = new long[usableSize]; - keysAndValues = new Object[usableSize * 2]; + protected ObjectHashMap(ObjectHashMap original) { + size = original.size; + usedHashes = original.usedHashes; + usedIndices = original.usedIndices; + metadata = PythonUtils.arrayCopyOf(original.metadata, original.metadata.length); + keysAndValues = PythonUtils.arrayCopyOf(original.keysAndValues, original.keysAndValues.length); + } + + private void allocateData(int bucketsCount) { + allocateData(bucketsCount, getUsableSize(bucketsCount)); + } + + private void allocateData(int bucketsCount, int entryCapacity) { + assert isPow2(bucketsCount); + assert entryCapacity > 0 && entryCapacity <= getUsableSize(bucketsCount); + ensureArraySizesFit(bucketsCount, entryCapacity); + metadata = createMetadata(bucketsCount, entryCapacity); + keysAndValues = new Object[entryCapacity * 2]; } public void clear() { @@ -241,17 +252,6 @@ public void clear() { allocateData(INITIAL_INDICES_SIZE); } - public ObjectHashMap copy() { - ObjectHashMap result = new ObjectHashMap(); - result.size = size; - result.usedHashes = usedHashes; - result.usedIndices = usedIndices; - result.hashes = PythonUtils.arrayCopyOf(hashes, hashes.length); - result.indices = PythonUtils.arrayCopyOf(indices, indices.length); - result.keysAndValues = PythonUtils.arrayCopyOf(keysAndValues, keysAndValues.length); - return result; - } - public MapCursor getEntries() { return new MapCursor(); } @@ -291,7 +291,7 @@ public boolean advance() { } public DictKey getKey() { - return new DictKey(ObjectHashMap.this.getKey(index), hashes[index]); + return new DictKey(ObjectHashMap.this.getKey(index), ObjectHashMap.this.getHash(index)); } public Object getValue() { @@ -299,15 +299,127 @@ public Object getValue() { } } - private static int getBucketsCount(int[] indices) { - return indices.length; + int getEntryCapacity() { + return keysAndValues.length >> 1; + } + + private static byte[] createMetadata(int bucketsCount, int usableSize) { + return new byte[getMetadataLength(bucketsCount, usableSize)]; + } + + private static int getBucketsCount(byte[] metadata, int entryCapacity, int indexByteSize) { + return (metadata.length - getIndicesOffset(entryCapacity)) / indexByteSize; + } + + static int getIndexByteSize(int entryCapacity) { + if (entryCapacity - 1 <= MAX_BYTE_INDEX) { + return Byte.BYTES; + } else if (entryCapacity - 1 <= MAX_SHORT_INDEX) { + return Short.BYTES; + } else { + return Integer.BYTES; + } } - private boolean needsResize(int[] localIndices) { - // when the hash table is 3/4 full, we resize on insertion - int bucketsCount = getBucketsCount(localIndices); - int bucketsCntQuarter = Math.max(1, bucketsCount >> 2); - return usedIndices + bucketsCntQuarter > bucketsCount; + @TruffleBoundary + private static int getIndexByteSizeAfterRestart(int entryCapacity) { + return getIndexByteSize(entryCapacity); + } + + private static int getIndicesOffset(int entryCapacity) { + return castMetadataInt(getIndicesOffsetLong(entryCapacity)); + } + + private static int getMetadataLength(int bucketsCount, int entryCapacity) { + return castMetadataInt(getMetadataLengthLong(bucketsCount, entryCapacity)); + } + + private static int getHashOffset(int index) { + return castMetadataInt((long) index * Long.BYTES); + } + + private static int getIndexOffset(int indicesOffset, int indexByteSize, int compactIndex) { + return indicesOffset + compactIndex * indexByteSize; + } + + private static long getIndicesOffsetLong(int entryCapacity) { + return (long) entryCapacity * Long.BYTES; + } + + private static long getMetadataLengthLong(int bucketsCount, int entryCapacity) { + return getIndicesOffsetLong(entryCapacity) + ((long) bucketsCount * getIndexByteSize(entryCapacity)); + } + + private static void ensureArraySizesFit(int bucketsCount, int entryCapacity) { + long metadataLength = getMetadataLengthLong(bucketsCount, entryCapacity); + if (metadataLength > Integer.MAX_VALUE || ((long) entryCapacity << 1) > Integer.MAX_VALUE) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new OutOfMemoryError(); + } + } + + private static int castMetadataInt(long value) { + assert value >= 0 && value <= Integer.MAX_VALUE; + return (int) value; + } + + private static int getPhysicalCollisionMaskForIndexByteSize(int indexByteSize) { + if (indexByteSize == Byte.BYTES) { + return BYTE_COLLISION_MASK; + } else if (indexByteSize == Short.BYTES) { + return SHORT_COLLISION_MASK; + } else { + return COLLISION_MASK; + } + } + + private static int getIndex(byte[] metadata, int indicesOffset, int indexByteSize, int compactIndex) { + int offset = getIndexOffset(indicesOffset, indexByteSize, compactIndex); + if (indexByteSize == Byte.BYTES) { + return decodeIndex(metadata[offset] & 0xFF, BYTE_COLLISION_MASK, BYTE_COLLISION_SHIFT); + } else if (indexByteSize == Short.BYTES) { + return decodeIndex(PythonUtils.ARRAY_ACCESSOR.getShort(metadata, offset) & 0xFFFF, SHORT_COLLISION_MASK, SHORT_COLLISION_SHIFT); + } else { + return decodeIndex(PythonUtils.ARRAY_ACCESSOR.getInt(metadata, offset), COLLISION_MASK, INT_COLLISION_SHIFT); + } + } + + private static int decodeIndex(int encodedValue, int physicalCollisionMask, int collisionShift) { + int value = encodedValue & (physicalCollisionMask - 1); + return value | ((encodedValue & physicalCollisionMask) << collisionShift); + } + + private static void setIndex(byte[] metadata, int indicesOffset, int indexByteSize, int physicalCollisionMask, int compactIndex, int logicalValue) { + int encodedValue = logicalValue & ~COLLISION_MASK; + if ((logicalValue & COLLISION_MASK) != 0) { + encodedValue |= physicalCollisionMask; + } + int offset = getIndexOffset(indicesOffset, indexByteSize, compactIndex); + if (indexByteSize == Byte.BYTES) { + metadata[offset] = (byte) encodedValue; + } else if (indexByteSize == Short.BYTES) { + PythonUtils.ARRAY_ACCESSOR.putShort(metadata, offset, (short) encodedValue); + } else { + PythonUtils.ARRAY_ACCESSOR.putInt(metadata, offset, encodedValue); + } + } + + private static long getHash(byte[] metadata, int index) { + return PythonUtils.ARRAY_ACCESSOR.getLong(metadata, getHashOffset(index)); + } + + private static void setHash(byte[] metadata, int index, long hash) { + PythonUtils.ARRAY_ACCESSOR.putLong(metadata, getHashOffset(index), hash); + } + + long getHash(int index) { + return getHash(metadata, index); + } + + private boolean needsResize(int entryCapacity, int bucketsCount) { + // Keep one slot empty at all times. For the smallest table, that means resizing once 2 of + // the 4 buckets are already in use instead of allowing the table to become completely full. + return usedHashes >= entryCapacity || usedIndices >= getUsableSize(bucketsCount); } public int size() { @@ -317,20 +429,33 @@ public int size() { @GenerateUncached @GenerateInline @GenerateCached(false) + @ImportStatic(ObjectHashMap.class) public abstract static class PopNode extends Node { public abstract Object[] execute(Node inliningTarget, ObjectHashMap map); - @Specialization - public static Object[] doPopWithRestart(Node inliningTarget, ObjectHashMap map, + @TruffleBoundary + public static Object[] doPopWithRestartForTests(ObjectHashMap map) { + // Public entry point for Java tests that bypass the generated node. + int entryCapacity = map.getEntryCapacity(); + return doPopWithRestart(null, map, entryCapacity, getIndexByteSize(entryCapacity), InlinedConditionProfile.getUncached(), + InlinedCountingConditionProfile.getUncached(), InlinedCountingConditionProfile.getUncached(), InlinedBranchProfile.getUncached()); + } + + @Specialization(guards = "indexByteSize == getIndexByteSize(entryCapacity)", limit = "INDEX_BYTE_SIZE_CACHE_LIMIT") + static Object[] doPopWithRestart(Node inliningTarget, ObjectHashMap map, + @Bind("map.getEntryCapacity()") int entryCapacity, + @Cached(value = "getIndexByteSize(entryCapacity)", allowUncached = true) int indexByteSize, @Cached InlinedConditionProfile emptyMapProfile, @Cached InlinedCountingConditionProfile hasValueProfile, @Cached InlinedCountingConditionProfile hasCollisionProfile, @Cached InlinedBranchProfile lookupRestart) { while (true) { try { - return doPop(inliningTarget, map, map.indices, emptyMapProfile, hasValueProfile, hasCollisionProfile); + return doPop(inliningTarget, map, map.metadata, entryCapacity, indexByteSize, emptyMapProfile, hasValueProfile, hasCollisionProfile); } catch (RestartLookupException ignore) { lookupRestart.enter(inliningTarget); + entryCapacity = map.getEntryCapacity(); + indexByteSize = getIndexByteSizeAfterRestart(entryCapacity); } } } @@ -339,7 +464,7 @@ private static boolean isIndex(int indexInIndices, int indexToFind) { return indexInIndices != DUMMY_INDEX && indexInIndices != EMPTY_INDEX && indexToFind == unwrapIndex(indexInIndices); } - private static Object[] doPop(Node inliningTarget, ObjectHashMap map, int[] indices, + private static Object[] doPop(Node inliningTarget, ObjectHashMap map, byte[] metadata, int entryCapacity, int indexByteSize, @Cached InlinedConditionProfile emptyMapProfile, @Cached InlinedCountingConditionProfile hasValueProfile, @Cached InlinedCountingConditionProfile hasCollisionProfile) throws RestartLookupException { @@ -347,9 +472,12 @@ private static Object[] doPop(Node inliningTarget, ObjectHashMap map, int[] indi return null; } Object[] localKeysAndValues = map.keysAndValues; + int indicesLen = getBucketsCount(metadata, entryCapacity, indexByteSize); + int indicesOffset = getIndicesOffset(entryCapacity); + int physicalCollisionMask = getPhysicalCollisionMaskForIndexByteSize(indexByteSize); int usedHashes = map.usedHashes; for (int i = usedHashes - 1; i >= 0; i--) { - if (indices != map.indices) { + if (metadata != map.metadata) { // restart, can happen after Truffle safepoint on backedge throw RestartLookupException.INSTANCE; } @@ -358,13 +486,13 @@ private static Object[] doPop(Node inliningTarget, ObjectHashMap map, int[] indi // We can remove the item from the compact arrays var result = new Object[]{map.getKey(i), value}; // We need to find the slot in the sparse indices array - long hash = map.hashes[i]; - int compactIndex = getIndex(indices.length, hash); - int index = indices[compactIndex]; + long hash = map.getHash(i); + int compactIndex = getIndex(indicesLen, hash); + int index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); if (hasCollisionProfile.profile(inliningTarget, isIndex(index, i))) { - indices[compactIndex] = DUMMY_INDEX; + setIndex(metadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex, DUMMY_INDEX); } else { - removeBucketWithIndex(map, indices, hash, compactIndex, i); + removeBucketWithIndex(map, metadata, indicesOffset, indexByteSize, physicalCollisionMask, indicesLen, hash, compactIndex, i); } // Only remove the slot now, removeBucketWithIndex can restart the search map.setValue(i, null); @@ -376,20 +504,23 @@ private static Object[] doPop(Node inliningTarget, ObjectHashMap map, int[] indi throw CompilerDirectives.shouldNotReachHere(); } - private static void removeBucketWithIndex(ObjectHashMap map, int[] indices, long hash, int initialCompactIndex, int indexToFind) throws RestartLookupException { - int searchLimit = getBucketsCount(map.indices) + PERTURB_SHIFTS_COUT; + private static void removeBucketWithIndex(ObjectHashMap map, byte[] metadata, int indicesOffset, int indexByteSize, int physicalCollisionMask, int indicesLen, long hash, + int initialCompactIndex, + int indexToFind) + throws RestartLookupException { + int searchLimit = indicesLen + PERTURB_SHIFTS_COUT; long perturb = hash; int compactIndex = initialCompactIndex; for (int i = 0; i < searchLimit; i++) { - if (indices != map.indices) { + if (metadata != map.metadata) { // guards against things happening in the safepoint on the backedge throw RestartLookupException.INSTANCE; } perturb >>>= PERTURB_SHIFT; - compactIndex = nextIndex(indices.length, compactIndex, perturb); - int index = indices[compactIndex]; + compactIndex = nextIndex(indicesLen, compactIndex, perturb); + int index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); if (isIndex(index, indexToFind)) { - indices[compactIndex] = DUMMY_INDEX; + setIndex(metadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex, DUMMY_INDEX); return; } } @@ -400,12 +531,23 @@ private static void removeBucketWithIndex(ObjectHashMap map, int[] indices, long @GenerateUncached @GenerateInline @GenerateCached(false) + @ImportStatic(ObjectHashMap.class) public abstract static class GetNode extends Node { public abstract Object execute(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash); - // "public" for testing... - @Specialization - public static Object doGetWithRestart(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, + @TruffleBoundary + public static Object doGetWithRestartForTests(ObjectHashMap map, Object key, long keyHash, PyObjectRichCompareBool eqNode) { + // Public entry point for Java tests that bypass the generated node. + int entryCapacity = map.getEntryCapacity(); + InlinedCountingConditionProfile uncachedCounting = InlinedCountingConditionProfile.getUncached(); + return doGetWithRestart(null, null, map, key, keyHash, entryCapacity, getIndexByteSize(entryCapacity), InlinedBranchProfile.getUncached(), uncachedCounting, + uncachedCounting, uncachedCounting, uncachedCounting, uncachedCounting, eqNode); + } + + @Specialization(guards = "indexByteSize == getIndexByteSize(entryCapacity)", limit = "INDEX_BYTE_SIZE_CACHE_LIMIT") + static Object doGetWithRestart(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, + @Bind("map.getEntryCapacity()") int entryCapacity, + @Cached(value = "getIndexByteSize(entryCapacity)", allowUncached = true) int indexByteSize, @Cached InlinedBranchProfile lookupRestart, @Cached InlinedCountingConditionProfile foundNullKey, @Cached InlinedCountingConditionProfile foundSameHashKey, @@ -415,16 +557,18 @@ public static Object doGetWithRestart(Frame frame, Node inliningTarget, ObjectHa @Cached PyObjectRichCompareBool eqNode) { while (true) { try { - return doGet(frame, map, key, keyHash, inliningTarget, foundNullKey, foundSameHashKey, + return doGet(frame, map, key, keyHash, indexByteSize, entryCapacity, inliningTarget, foundNullKey, foundSameHashKey, foundEqKey, collisionFoundNoValue, collisionFoundEqKey, eqNode); } catch (RestartLookupException ignore) { lookupRestart.enter(inliningTarget); TruffleSafepoint.poll(inliningTarget); + entryCapacity = map.getEntryCapacity(); + indexByteSize = getIndexByteSizeAfterRestart(entryCapacity); } } } - static Object doGet(Frame frame, ObjectHashMap map, Object key, long keyHash, + static Object doGet(Frame frame, ObjectHashMap map, Object key, long keyHash, int indexByteSize, int entryCapacity, Node inliningTarget, InlinedCountingConditionProfile foundNullKey, InlinedCountingConditionProfile foundSameHashKey, @@ -432,57 +576,59 @@ static Object doGet(Frame frame, ObjectHashMap map, Object key, long keyHash, InlinedCountingConditionProfile collisionFoundNoValue, InlinedCountingConditionProfile collisionFoundEqKey, PyObjectRichCompareBool eqNode) throws RestartLookupException { - assert map.checkInternalState(); - int[] indices = map.indices; - int indicesLen = indices.length; + assert map.checkInternalState(entryCapacity, indexByteSize); + byte[] metadata = map.metadata; + int indicesLen = getBucketsCount(metadata, entryCapacity, indexByteSize); + int indicesOffset = getIndicesOffset(entryCapacity); int compactIndex = getIndex(indicesLen, keyHash); - int index = indices[compactIndex]; + int index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); if (foundNullKey.profile(inliningTarget, index == EMPTY_INDEX)) { return null; } if (foundSameHashKey.profile(inliningTarget, index != DUMMY_INDEX)) { int unwrappedIndex = unwrapIndex(index); - if (foundEqKey.profile(inliningTarget, map.keysEqual(indices, frame, inliningTarget, unwrappedIndex, key, keyHash, eqNode))) { + if (foundEqKey.profile(inliningTarget, map.keysEqual(metadata, frame, inliningTarget, unwrappedIndex, key, keyHash, eqNode))) { return map.getValue(unwrappedIndex); - } else if (!isCollision(indices[compactIndex])) { - // ^ note: we need to re-read indices[compactIndex], + } else if (!isCollision(getIndex(metadata, indicesOffset, indexByteSize, compactIndex))) { + // ^ note: we need to re-read the bucket, // it may have been changed during __eq__ return null; } } - return getCollision(frame, map, key, keyHash, inliningTarget, collisionFoundNoValue, collisionFoundEqKey, eqNode, indices, indicesLen, compactIndex); + return getCollision(frame, map, key, keyHash, inliningTarget, collisionFoundNoValue, collisionFoundEqKey, eqNode, metadata, indicesOffset, indexByteSize, indicesLen, + compactIndex); } @InliningCutoff private static Object getCollision(Frame frame, ObjectHashMap map, Object key, long keyHash, Node inliningTarget, InlinedCountingConditionProfile collisionFoundNoValue, InlinedCountingConditionProfile collisionFoundEqKey, - PyObjectRichCompareBool eqNode, int[] indices, int indicesLen, int compactIndex) throws RestartLookupException { + PyObjectRichCompareBool eqNode, byte[] metadata, int indicesOffset, int indexByteSize, int indicesLen, int compactIndex) throws RestartLookupException { int index; // collision: intentionally counted loop long perturb = keyHash; - int searchLimit = getBucketsCount(indices) + PERTURB_SHIFTS_COUT; + int searchLimit = indicesLen + PERTURB_SHIFTS_COUT; int i = 0; try { for (; i < searchLimit; i++) { - if (indices != map.indices) { + if (metadata != map.metadata) { // guards against things happening in the safepoint on the backedge throw RestartLookupException.INSTANCE; } perturb >>>= PERTURB_SHIFT; compactIndex = nextIndex(indicesLen, compactIndex, perturb); - index = map.indices[compactIndex]; + index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); if (collisionFoundNoValue.profile(inliningTarget, index == EMPTY_INDEX)) { return null; } if (index != DUMMY_INDEX) { int unwrappedIndex = unwrapIndex(index); - if (collisionFoundEqKey.profile(inliningTarget, map.keysEqual(indices, frame, inliningTarget, unwrappedIndex, key, keyHash, eqNode))) { + if (collisionFoundEqKey.profile(inliningTarget, map.keysEqual(metadata, frame, inliningTarget, unwrappedIndex, key, keyHash, eqNode))) { return map.getValue(unwrappedIndex); - } else if (!isCollision(indices[compactIndex])) { - // ^ note: we need to re-read indices[compactIndex], + } else if (!isCollision(getIndex(metadata, indicesOffset, indexByteSize, compactIndex))) { + // ^ note: we need to re-read the bucket, // it may have been changed during __eq__ return null; } @@ -502,6 +648,7 @@ private static Object getCollision(Frame frame, ObjectHashMap map, Object key, l @GenerateUncached @GenerateInline @GenerateCached(false) + @ImportStatic(ObjectHashMap.class) public abstract static class PutNode extends Node { public static PutNode getUncached() { return PutNodeGen.getUncached(); @@ -521,9 +668,19 @@ public static void putUncached(ObjectHashMap map, Object key, long keyHash, Obje abstract void execute(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, Object value); - // "public" for testing... - @Specialization - public static void doPutWithRestart(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, Object value, + @TruffleBoundary + public static void doPutWithRestartForTests(ObjectHashMap map, Object key, long keyHash, Object value, PyObjectRichCompareBool eqNode) { + // Public entry point for Java tests that bypass the generated node. + int entryCapacity = map.getEntryCapacity(); + InlinedCountingConditionProfile uncachedCounting = InlinedCountingConditionProfile.getUncached(); + doPutWithRestart(null, null, map, key, keyHash, value, entryCapacity, getIndexByteSize(entryCapacity), InlinedBranchProfile.getUncached(), uncachedCounting, + uncachedCounting, uncachedCounting, uncachedCounting, InlinedBranchProfile.getUncached(), InlinedBranchProfile.getUncached(), eqNode); + } + + @Specialization(guards = "indexByteSize == getIndexByteSize(entryCapacity)", limit = "INDEX_BYTE_SIZE_CACHE_LIMIT") + static void doPutWithRestart(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, Object value, + @Bind("map.getEntryCapacity()") int entryCapacity, + @Cached(value = "getIndexByteSize(entryCapacity)", allowUncached = true) int indexByteSize, @Cached InlinedBranchProfile lookupRestart, @Cached InlinedCountingConditionProfile foundNullKey, @Cached InlinedCountingConditionProfile foundEqKey, @@ -534,18 +691,20 @@ public static void doPutWithRestart(Frame frame, Node inliningTarget, ObjectHash @Cached PyObjectRichCompareBool eqNode) { while (true) { try { - doPut(frame, map, key, keyHash, value, inliningTarget, foundNullKey, foundEqKey, + doPut(frame, map, key, keyHash, value, entryCapacity, indexByteSize, inliningTarget, foundNullKey, foundEqKey, collisionFoundNoValue, collisionFoundEqKey, rehash1Profile, rehash2Profile, eqNode); return; } catch (RestartLookupException ignore) { lookupRestart.enter(inliningTarget); TruffleSafepoint.poll(inliningTarget); + entryCapacity = map.getEntryCapacity(); + indexByteSize = getIndexByteSizeAfterRestart(entryCapacity); } } } - static void doPut(Frame frame, ObjectHashMap map, Object key, long keyHash, Object value, + static void doPut(Frame frame, ObjectHashMap map, Object key, long keyHash, Object value, int entryCapacity, int indexByteSize, Node inliningTarget, InlinedCountingConditionProfile foundNullKey, InlinedCountingConditionProfile foundEqKey, @@ -554,55 +713,59 @@ static void doPut(Frame frame, ObjectHashMap map, Object key, long keyHash, Obje InlinedBranchProfile rehash1Profile, InlinedBranchProfile rehash2Profile, PyObjectRichCompareBool eqNode) throws RestartLookupException { - assert map.checkInternalState(); - int[] indices = map.indices; - int indicesLen = indices.length; + assert map.checkInternalState(entryCapacity, indexByteSize); + byte[] metadata = map.metadata; + int indicesLen = getBucketsCount(metadata, entryCapacity, indexByteSize); + int indicesOffset = getIndicesOffset(entryCapacity); + int physicalCollisionMask = getPhysicalCollisionMaskForIndexByteSize(indexByteSize); int compactIndex = getIndex(indicesLen, keyHash); - int index = indices[compactIndex]; + int index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); if (foundNullKey.profile(inliningTarget, index == EMPTY_INDEX)) { - map.putInNewSlot(indices, inliningTarget, rehash1Profile, key, keyHash, value, compactIndex); + map.putInNewSlot(metadata, indicesOffset, indexByteSize, physicalCollisionMask, entryCapacity, indicesLen, inliningTarget, rehash1Profile, key, keyHash, value, compactIndex); return; } - if (foundEqKey.profile(inliningTarget, index != DUMMY_INDEX && map.keysEqual(indices, frame, inliningTarget, unwrapIndex(index), key, keyHash, eqNode))) { + if (foundEqKey.profile(inliningTarget, index != DUMMY_INDEX && map.keysEqual(metadata, frame, inliningTarget, unwrapIndex(index), key, keyHash, eqNode))) { // we found the key, override the value, Python does not override the key though map.setValue(unwrapIndex(index), value); return; } - putCollision(frame, map, key, keyHash, value, inliningTarget, collisionFoundNoValue, collisionFoundEqKey, rehash2Profile, eqNode, indices, indicesLen, compactIndex); + putCollision(frame, map, key, keyHash, value, inliningTarget, collisionFoundNoValue, collisionFoundEqKey, rehash2Profile, eqNode, metadata, indicesOffset, indexByteSize, + physicalCollisionMask, entryCapacity, indicesLen, compactIndex); } @InliningCutoff private static void putCollision(Frame frame, ObjectHashMap map, Object key, long keyHash, Object value, Node inliningTarget, InlinedCountingConditionProfile collisionFoundNoValue, InlinedCountingConditionProfile collisionFoundEqKey, InlinedBranchProfile rehash2Profile, PyObjectRichCompareBool eqNode, - int[] indices, int indicesLen, int compactIndex) throws RestartLookupException { - markCollision(indices, compactIndex); + byte[] metadata, int indicesOffset, int indexByteSize, int physicalCollisionMask, int entryCapacity, int indicesLen, int compactIndex) + throws RestartLookupException { + markCollision(metadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex); long perturb = keyHash; - int searchLimit = getBucketsCount(indices) + PERTURB_SHIFTS_COUT; + int searchLimit = indicesLen + PERTURB_SHIFTS_COUT; int i = 0; try { for (; i < searchLimit; i++) { - if (indices != map.indices) { + if (metadata != map.metadata) { // guards against things happening in the safepoint on the backedge throw RestartLookupException.INSTANCE; } perturb >>>= PERTURB_SHIFT; compactIndex = nextIndex(indicesLen, compactIndex, perturb); - int index = indices[compactIndex]; + int index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); if (collisionFoundNoValue.profile(inliningTarget, index == EMPTY_INDEX)) { - map.putInNewSlot(indices, inliningTarget, rehash2Profile, key, keyHash, value, compactIndex); + map.putInNewSlot(metadata, indicesOffset, indexByteSize, physicalCollisionMask, entryCapacity, indicesLen, inliningTarget, rehash2Profile, key, keyHash, value, compactIndex); return; } - if (collisionFoundEqKey.profile(inliningTarget, index != DUMMY_INDEX && map.keysEqual(indices, frame, inliningTarget, unwrapIndex(index), key, keyHash, eqNode))) { + if (collisionFoundEqKey.profile(inliningTarget, index != DUMMY_INDEX && map.keysEqual(metadata, frame, inliningTarget, unwrapIndex(index), key, keyHash, eqNode))) { // we found the key, override the value, Python does not override the key // though map.setValue(unwrapIndex(index), value); return; } - markCollision(indices, compactIndex); + markCollision(metadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex); TruffleSafepoint.poll(inliningTarget); } } finally { @@ -618,28 +781,28 @@ private static void putCollision(Frame frame, ObjectHashMap map, Object key, lon // Internal helper: it is not profiling, never rehashes, and it assumes that the hash map never // contains the key that we are inserting - private void insertNewKey(int[] localIndices, Object key, long keyHash, Object value) { - assert localIndices == this.indices; - int compactIndex = getIndex(localIndices.length, keyHash); - int index = localIndices[compactIndex]; + private void insertNewKey(byte[] localMetadata, int indicesLen, int indicesOffset, int indexByteSize, int physicalCollisionMask, Object key, long keyHash, Object value) { + assert localMetadata == this.metadata; + int compactIndex = getIndex(indicesLen, keyHash); + int index = getIndex(localMetadata, indicesOffset, indexByteSize, compactIndex); if (index == EMPTY_INDEX) { - putInNewSlot(localIndices, key, keyHash, value, compactIndex); + putInNewSlot(localMetadata, indicesOffset, indexByteSize, physicalCollisionMask, key, keyHash, value, compactIndex); return; } // collision - markCollision(localIndices, compactIndex); + markCollision(localMetadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex); long perturb = keyHash; - int searchLimit = getBucketsCount(localIndices) + PERTURB_SHIFTS_COUT; + int searchLimit = indicesLen + PERTURB_SHIFTS_COUT; for (int i = 0; i < searchLimit; i++) { perturb >>>= PERTURB_SHIFT; - compactIndex = nextIndex(localIndices.length, compactIndex, perturb); - index = localIndices[compactIndex]; + compactIndex = nextIndex(indicesLen, compactIndex, perturb); + index = getIndex(localMetadata, indicesOffset, indexByteSize, compactIndex); if (index == EMPTY_INDEX) { - putInNewSlot(localIndices, key, keyHash, value, compactIndex); + putInNewSlot(localMetadata, indicesOffset, indexByteSize, physicalCollisionMask, key, keyHash, value, compactIndex); return; } - markCollision(localIndices, compactIndex); + markCollision(localMetadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex); } // all values are dummies? Not possible, since we should have compacted the // hashes/keysAndValues arrays in "remove". Also, there must be an unused slot available, @@ -647,29 +810,31 @@ private void insertNewKey(int[] localIndices, Object key, long keyHash, Object v throw CompilerDirectives.shouldNotReachHere(); } - private void putInNewSlot(int[] localIndices, Node inliningTarget, InlinedBranchProfile rehashProfile, Object key, long keyHash, Object value, int compactIndex) { - assert indices == localIndices; - if (CompilerDirectives.injectBranchProbability(SLOWPATH_PROBABILITY, needsResize(localIndices))) { + private void putInNewSlot(byte[] localMetadata, int indicesOffset, int indexByteSize, int physicalCollisionMask, int entryCapacity, int bucketsCount, Node inliningTarget, + InlinedBranchProfile rehashProfile, Object key, long keyHash, Object value, int compactIndex) { + assert metadata == localMetadata; + assert entryCapacity == getEntryCapacity(); + if (CompilerDirectives.injectBranchProbability(SLOWPATH_PROBABILITY, needsResize(entryCapacity, bucketsCount))) { rehashProfile.enter(inliningTarget); rehashAndPut(key, keyHash, value); return; } - putInNewSlot(localIndices, key, keyHash, value, compactIndex); + putInNewSlot(localMetadata, indicesOffset, indexByteSize, physicalCollisionMask, key, keyHash, value, compactIndex); } - private void putInNewSlot(int[] localIndices, Object key, long keyHash, Object value, int compactIndex) { + private void putInNewSlot(byte[] localMetadata, int indicesOffset, int indexByteSize, int physicalCollisionMask, Object key, long keyHash, Object value, int compactIndex) { size++; usedIndices++; int newIndex = usedHashes++; - localIndices[compactIndex] = newIndex; + setIndex(localMetadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex, newIndex + INDEX_OFFSET); setValue(newIndex, value); setKey(newIndex, key); - hashes[newIndex] = keyHash; + setHash(localMetadata, newIndex, keyHash); } - private boolean needsCompaction() { + private boolean needsCompaction(int entryCapacity) { // if more than quarter of all the slots are occupied by dummy values -> compact - int quarterOfUsable = hashes.length >> 2; + int quarterOfUsable = entryCapacity >> 2; int dummyCnt = usedHashes - size; return dummyCnt > quarterOfUsable; } @@ -677,6 +842,7 @@ private boolean needsCompaction() { @GenerateUncached @GenerateInline @GenerateCached(false) + @ImportStatic(ObjectHashMap.class) public abstract static class RemoveNode extends Node { public static Object removeUncached(ObjectHashMap map, Object key, long keyHash) { return RemoveNodeGen.getUncached().execute(null, null, map, key, keyHash); @@ -684,9 +850,19 @@ public static Object removeUncached(ObjectHashMap map, Object key, long keyHash) public abstract Object execute(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash); - // "public" for testing... - @Specialization - public static Object doRemoveWithRestart(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, + @TruffleBoundary + public static Object doRemoveWithRestartForTests(ObjectHashMap map, Object key, long keyHash, PyObjectRichCompareBool eqNode) { + // Public entry point for Java tests that bypass the generated node. + int entryCapacity = map.getEntryCapacity(); + InlinedCountingConditionProfile uncachedCounting = InlinedCountingConditionProfile.getUncached(); + return doRemoveWithRestart(null, null, map, key, keyHash, entryCapacity, getIndexByteSize(entryCapacity), InlinedBranchProfile.getUncached(), uncachedCounting, + uncachedCounting, uncachedCounting, uncachedCounting, InlinedBranchProfile.getUncached(), eqNode); + } + + @Specialization(guards = "indexByteSize == getIndexByteSize(entryCapacity)", limit = "INDEX_BYTE_SIZE_CACHE_LIMIT") + static Object doRemoveWithRestart(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, + @Bind("map.getEntryCapacity()") int entryCapacity, + @Cached(value = "getIndexByteSize(entryCapacity)", allowUncached = true) int indexByteSize, @Cached InlinedBranchProfile lookupRestart, @Cached InlinedCountingConditionProfile foundNullKey, @Cached InlinedCountingConditionProfile foundEqKey, @@ -696,43 +872,47 @@ public static Object doRemoveWithRestart(Frame frame, Node inliningTarget, Objec @Cached PyObjectRichCompareBool eqNode) { while (true) { try { - return doRemove(frame, inliningTarget, map, key, keyHash, foundNullKey, foundEqKey, + return doRemove(frame, inliningTarget, map, key, keyHash, entryCapacity, indexByteSize, foundNullKey, foundEqKey, collisionFoundNoValue, collisionFoundEqKey, compactProfile, eqNode); } catch (RestartLookupException ignore) { lookupRestart.enter(inliningTarget); + entryCapacity = map.getEntryCapacity(); + indexByteSize = getIndexByteSizeAfterRestart(entryCapacity); } } } - static Object doRemove(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, + static Object doRemove(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, int entryCapacity, int indexByteSize, InlinedCountingConditionProfile foundNullKey, InlinedCountingConditionProfile foundEqKey, InlinedCountingConditionProfile collisionFoundNoValue, InlinedCountingConditionProfile collisionFoundEqKey, InlinedBranchProfile compactProfile, PyObjectRichCompareBool eqNode) throws RestartLookupException { - assert map.checkInternalState(); + assert map.checkInternalState(entryCapacity, indexByteSize); // TODO: move this to the point after we find the value to remove? - if (CompilerDirectives.injectBranchProbability(SLOWPATH_PROBABILITY, map.needsCompaction())) { + if (CompilerDirectives.injectBranchProbability(SLOWPATH_PROBABILITY, map.needsCompaction(entryCapacity))) { compactProfile.enter(inliningTarget); map.compact(); } - int[] indices = map.indices; - int indicesLen = indices.length; + byte[] metadata = map.metadata; + int indicesLen = getBucketsCount(metadata, entryCapacity, indexByteSize); + int indicesOffset = getIndicesOffset(entryCapacity); + int physicalCollisionMask = getPhysicalCollisionMaskForIndexByteSize(indexByteSize); // Note: CPython is not shrinking the capacity of the hash table on delete, we do the // same int compactIndex = getIndex(indicesLen, keyHash); - int index = indices[compactIndex]; + int index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); if (foundNullKey.profile(inliningTarget, index == EMPTY_INDEX)) { return null; // not found } int unwrappedIndex = unwrapIndex(index); - if (foundEqKey.profile(inliningTarget, index != DUMMY_INDEX && map.keysEqual(indices, frame, inliningTarget, unwrappedIndex, key, keyHash, eqNode))) { + if (foundEqKey.profile(inliningTarget, index != DUMMY_INDEX && map.keysEqual(metadata, frame, inliningTarget, unwrappedIndex, key, keyHash, eqNode))) { Object result = map.getValue(unwrappedIndex); - indices[compactIndex] = DUMMY_INDEX; + setIndex(metadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex, DUMMY_INDEX); map.setValue(unwrappedIndex, null); map.setKey(unwrappedIndex, null); map.size--; @@ -740,33 +920,35 @@ static Object doRemove(Frame frame, Node inliningTarget, ObjectHashMap map, Obje } // collision: intentionally counted loop - return removeCollision(frame, inliningTarget, map, key, keyHash, collisionFoundNoValue, collisionFoundEqKey, eqNode, indices, indicesLen, compactIndex); + return removeCollision(frame, inliningTarget, map, key, keyHash, collisionFoundNoValue, collisionFoundEqKey, eqNode, metadata, indicesOffset, indexByteSize, + physicalCollisionMask, indicesLen, compactIndex); } @InliningCutoff private static Object removeCollision(Frame frame, Node inliningTarget, ObjectHashMap map, Object key, long keyHash, InlinedCountingConditionProfile collisionFoundNoValue, InlinedCountingConditionProfile collisionFoundEqKey, - PyObjectRichCompareBool eqNode, int[] indices, int indicesLen, int compactIndex) throws RestartLookupException { + PyObjectRichCompareBool eqNode, byte[] metadata, int indicesOffset, int indexByteSize, int physicalCollisionMask, int indicesLen, int compactIndex) + throws RestartLookupException { int unwrappedIndex; long perturb = keyHash; - int searchLimit = getBucketsCount(indices) + PERTURB_SHIFTS_COUT; + int searchLimit = indicesLen + PERTURB_SHIFTS_COUT; int i = 0; try { for (; i < searchLimit; i++) { - if (indices != map.indices) { + if (metadata != map.metadata) { // guards against things happening in the safepoint on the backedge throw RestartLookupException.INSTANCE; } perturb >>>= PERTURB_SHIFT; compactIndex = nextIndex(indicesLen, compactIndex, perturb); - int index = indices[compactIndex]; + int index = getIndex(metadata, indicesOffset, indexByteSize, compactIndex); if (collisionFoundNoValue.profile(inliningTarget, index == EMPTY_INDEX)) { return null; } unwrappedIndex = unwrapIndex(index); - if (collisionFoundEqKey.profile(inliningTarget, index != DUMMY_INDEX && map.keysEqual(indices, frame, inliningTarget, unwrappedIndex, key, keyHash, eqNode))) { + if (collisionFoundEqKey.profile(inliningTarget, index != DUMMY_INDEX && map.keysEqual(metadata, frame, inliningTarget, unwrappedIndex, key, keyHash, eqNode))) { Object result = map.getValue(unwrappedIndex); - indices[compactIndex] = DUMMY_INDEX; + setIndex(metadata, indicesOffset, indexByteSize, physicalCollisionMask, compactIndex, DUMMY_INDEX); map.setValue(unwrappedIndex, null); map.setKey(unwrappedIndex, null); map.size--; @@ -797,9 +979,9 @@ public Throwable fillInStackTrace() { } } - private boolean keysEqual(int[] originalIndices, Frame frame, Node inliningTarget, int index, Object key, long keyHash, + private boolean keysEqual(byte[] originalMetadata, Frame frame, Node inliningTarget, int index, Object key, long keyHash, PyObjectRichCompareBool eqNode) throws RestartLookupException { - if (hashes[index] != keyHash) { + if (getHash(index) != keyHash) { return false; } Object originalKey = getKey(index); @@ -807,7 +989,7 @@ private boolean keysEqual(int[] originalIndices, Frame frame, Node inliningTarge return true; } boolean result = eqNode.executeEq(frame, inliningTarget, originalKey, key); - if (indices != originalIndices || getKey(index) != originalKey) { + if (metadata != originalMetadata || getKey(index) != originalKey) { // Either someone overridden the slot we are just examining, or rehasing reallocated the // indices array. We need to restart the lookup. Other situations are OK: // @@ -832,42 +1014,44 @@ private boolean keysEqual(int[] originalIndices, Frame frame, Node inliningTarge */ @TruffleBoundary private void rehashAndPut(Object newKey, long newKeyHash, Object newValue) { - int requiredIndicesSize = usedHashes * GROWTH_RATE; - // We need the hash table of this size, in order to accommodate "requiredIndicesSize" items - int indicesCapacity = requiredIndicesSize + (requiredIndicesSize / 3); - if (indicesCapacity < INITIAL_INDICES_SIZE) { - indicesCapacity = INITIAL_INDICES_SIZE; - } else { - indicesCapacity = getNextPow2(indicesCapacity); - if (indicesCapacity << 1 < 0) { - // some arrays we allocate are 2 times the size - throw new OutOfMemoryError(); - } - } - long[] oldHashes = hashes; + int newSize = size + 1; + int indicesCapacity = getMinBucketsCount(newSize); + byte[] oldMetadata = metadata; Object[] oldKeysAndValues = keysAndValues; int oldUsedSize = usedHashes; int oldSize = size; - allocateData(indicesCapacity); + allocateData(indicesCapacity, getRequestedEntryCapacity(newSize, indicesCapacity)); size = 0; usedHashes = 0; usedIndices = 0; - int[] localIndices = this.indices; + byte[] localMetadata = this.metadata; + int entryCapacity = getEntryCapacity(); + int indexByteSize = getIndexByteSize(entryCapacity); + int indicesLen = getBucketsCount(localMetadata, entryCapacity, indexByteSize); + int indicesOffset = getIndicesOffset(entryCapacity); + int physicalCollisionMask = getPhysicalCollisionMaskForIndexByteSize(indexByteSize); for (int i = 0; i < oldUsedSize; i++) { if (getValue(i, oldKeysAndValues) != null) { final Object key = getKey(i, oldKeysAndValues); - insertNewKey(localIndices, key, oldHashes[i], getValue(i, oldKeysAndValues)); + insertNewKey(localMetadata, indicesLen, indicesOffset, indexByteSize, physicalCollisionMask, key, getHash(oldMetadata, i), getValue(i, oldKeysAndValues)); } } assert size == oldSize : String.format("size=%d, oldSize=%d, oldUsedSize=%d, usedHashes=%d, usedIndices=%d", size, oldSize, oldUsedSize, usedHashes, usedIndices); - insertNewKey(localIndices, newKey, newKeyHash, newValue); + insertNewKey(localMetadata, indicesLen, indicesOffset, indexByteSize, physicalCollisionMask, newKey, newKeyHash, newValue); + } + + private static int getRequestedEntryCapacity(int requestedCapacity, int bucketsCount) { + if (requestedCapacity <= TIGHT_ENTRY_CAPACITY_LIMIT) { + return requestedCapacity; + } + return getUsableSize(bucketsCount); } @TruffleBoundary private void compact() { // shuffle[X] will tell us by how much value X found in 'indices' should be shuffled to left - int[] shuffle = new int[hashes.length]; + int[] shuffle = new int[getEntryCapacity()]; int currentShuffle = 0; int dummyCount = 0; for (int i = 0; i < usedHashes; i++) { @@ -882,21 +1066,27 @@ private void compact() { setKey(i - currentShuffle, getKey(i)); setValue(i, null); setKey(i, null); - hashes[i - currentShuffle] = hashes[i]; + setHash(metadata, i - currentShuffle, getHash(i)); shuffle[i] = currentShuffle; } } usedHashes -= dummyCount; // We've "removed" the dummy entries - int[] localIndices = indices; - for (int i = 0; i < localIndices.length; i++) { - int index = localIndices[i]; + byte[] localMetadata = metadata; + int entryCapacity = getEntryCapacity(); + int indexByteSize = getIndexByteSize(entryCapacity); + int localIndicesLength = getBucketsCount(localMetadata, entryCapacity, indexByteSize); + int indicesOffset = getIndicesOffset(entryCapacity); + int physicalCollisionMask = getPhysicalCollisionMaskForIndexByteSize(indexByteSize); + for (int i = 0; i < localIndicesLength; i++) { + int index = getIndex(localMetadata, indicesOffset, indexByteSize, i); if (index != EMPTY_INDEX && index != DUMMY_INDEX) { boolean collision = isCollision(index); int unwrapped = unwrapIndex(index); int newIndex = unwrapped - shuffle[unwrapped]; - localIndices[i] = newIndex; if (collision) { - markCollision(localIndices, i); + setIndex(localMetadata, indicesOffset, indexByteSize, physicalCollisionMask, i, newIndex + INDEX_OFFSET | COLLISION_MASK); + } else { + setIndex(localMetadata, indicesOffset, indexByteSize, physicalCollisionMask, i, newIndex + INDEX_OFFSET); } } else if (index == DUMMY_INDEX) { dummyCount--; @@ -916,6 +1106,24 @@ private static int getIndex(int indicesLen, long hash) { return (int) (hash & (indicesLen - 1)); } + private static int getUsableSize(int bucketsCount) { + int minFreeBuckets = Math.max(2, bucketsCount >> 2); + return bucketsCount - minFreeBuckets + 1; + } + + private static int getMinBucketsCount(int requiredEntries) { + int bucketsCount = INITIAL_INDICES_SIZE; + while (getUsableSize(bucketsCount) < requiredEntries) { + if (bucketsCount > Integer.MAX_VALUE >> 1) { + // The backing arrays cannot grow past Java array indexing limits. The exact packed + // metadata bound is checked in allocateData. + throw new OutOfMemoryError(); + } + bucketsCount <<= 1; + } + return bucketsCount; + } + public static Object getKey(int index, Object[] keysAndValues) { return keysAndValues[index << 1]; } @@ -940,20 +1148,13 @@ public void setKey(int index, Object key) { keysAndValues[(index << 1)] = key; } - private boolean checkInternalState() { + private boolean checkInternalState(int entryCapacity, int indexByteSize) { // We must have at least one empty slot, collision resolution relies on the fact that it is // always going to find an empty slot - assert usedIndices < indices.length : usedIndices; + assert usedIndices < getBucketsCount(metadata, entryCapacity, indexByteSize) : usedIndices; return true; } - private static int getNextPow2(int n) { - if (isPow2(n)) { - return n; - } - return 1 << (Integer.SIZE - Integer.numberOfLeadingZeros(n)); - } - private static boolean isPow2(int n) { return Integer.bitCount(n) == 1; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceNodes.java index fa9a34f047..31610f632e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceNodes.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,6 +44,7 @@ import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; 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.common.SequenceNodesFactory.CachedGetObjectArrayNodeGen; import com.oracle.graal.python.builtins.objects.common.SequenceNodesFactory.GetObjectArrayNodeGen; import com.oracle.graal.python.builtins.objects.common.SequenceNodesFactory.SetSequenceStorageNodeGen; @@ -51,9 +52,11 @@ import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PySequenceCheckNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; 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.builtins.TupleNodes; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.runtime.sequence.PSequence; import com.oracle.graal.python.runtime.sequence.storage.ForeignSequenceStorage; @@ -137,6 +140,13 @@ static SequenceStorage doSequence(Node inliningTarget, PSequence seq, return getPSequenceStorageNode.execute(inliningTarget, seq); } + @Specialization(guards = "tupleCheck.execute(inliningTarget, seq)", limit = "1") + static SequenceStorage doNativeTuple(Node inliningTarget, PythonAbstractNativeObject seq, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @Cached TupleNodes.GetNativeTupleStorage getNativeTupleStorage) { + return getNativeTupleStorage.execute(seq); + } + // Note: this does not seem currently used but is good to accept foreign lists in more // places @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, seq)", "interop.hasArrayElements(seq)"}, limit = "1") 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..a9c404d528 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; @@ -47,8 +58,8 @@ import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; import com.oracle.graal.python.builtins.objects.bytes.PBytesLike; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes; -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.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.common.IndexNodes.NormalizeIndexCustomMessageNode; import com.oracle.graal.python.builtins.objects.common.IndexNodes.NormalizeIndexNode; @@ -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,14 @@ 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)); + @Bind Node inliningTarget, + @Cached NativeToPythonInternalNode toJavaNode) { + return toJavaNode.execute(inliningTarget, 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 +808,22 @@ 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) { + @Bind Node inliningTarget, + @Cached NativeToPythonInternalNode 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.execute(inliningTarget, readPtrArrayElement(storage.getPtr(), i)); } return new ObjectSequenceStorage(newArray); } @@ -1253,7 +1267,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 +1287,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 +1381,19 @@ 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.EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode toNative, @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); + Object promoted = ensurePythonObjectNode.execute(PythonContext.get(inliningTarget), value, false); + writePtrArrayElement(storage.getPtr(), idx, toNative.executeNewRef(inliningTarget, promoted)); decRefPointerNode.execute(inliningTarget, old); } } @@ -1393,16 +1406,17 @@ 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)); + @Bind Node inliningTarget, + @Cached CExtNodes.EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode toNative) { + Object promoted = ensurePythonObjectNode.execute(PythonContext.get(inliningTarget), value, false); + writePtrArrayElement(storage.getPtr(), idx, toNative.executeNewRef(inliningTarget, promoted)); } } @@ -1635,39 +1649,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 +1865,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 +3220,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 +3464,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 +3582,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 +4075,15 @@ 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) { + @Cached CExtNodes.EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode 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)); + Object promoted = ensurePythonObjectNode.execute(PythonContext.get(inliningTarget), value, false); + writePtrArrayElement(storage.getPtr(), index, toNative.executeNewRef(inliningTarget, promoted)); storage.setNewLength(newLength); return storage; } @@ -4115,15 +4091,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 dfb6d10810..d826018d60 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 @@ -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 @@ -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; @@ -103,6 +107,7 @@ import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; @@ -111,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; @@ -182,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); } @@ -235,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) { @@ -250,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 CApiTransitions.PythonToNativeInternalNode toNativeNode, + @Cached CApiTransitions.NativeToPythonInternalNode toPythonNode, + @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.execute(inliningTarget, 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.executeTransfer(inliningTarget, nativeResult)); + } finally { + Reference.reachabilityFence(cls); + } } } @@ -617,7 +634,11 @@ private Object callComplex(VirtualFrame frame, Object object) { CompilerDirectives.transferToInterpreterAndInvalidate(); callComplexNode = insert(LookupAndCallUnaryNode.create(T___COMPLEX__)); } - return callComplexNode.executeObject(frame, object); + try { + return callComplexNode.executeObject(frame, object); + } catch (SpecialMethodNotFound e) { + return null; + } } private WarningsModuleBuiltins.WarnNode getWarnNode() { @@ -649,7 +670,7 @@ private PComplex getComplexNumberFromObject(VirtualFrame frame, Object object, N object, "__complex__", "complex", result, "complex"); } return (PComplex) result; - } else if (result != PNone.NO_VALUE) { + } else if (result != null) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.COMPLEX_RETURNED_NON_COMPLEX, result); } if (object instanceof PComplex) { @@ -961,13 +982,25 @@ abstract static class AddNode extends BinaryOpBuiltinNode { @Specialization static PComplex doInt(PComplex left, int right, @Bind PythonLanguage language) { - return PFactory.createComplex(language, left.getReal() + right, left.getImag()); + return PFactory.createComplex(language, left.getReal() + right, left.getImag() + 0.0); } @Specialization static PComplex doDouble(PComplex left, double right, @Bind PythonLanguage language) { - return PFactory.createComplex(language, left.getReal() + right, left.getImag()); + return PFactory.createComplex(language, left.getReal() + right, left.getImag() + 0.0); + } + + @Specialization + static PComplex doInt(int left, PComplex right, + @Bind PythonLanguage language) { + return PFactory.createComplex(language, left + right.getReal(), 0.0 + right.getImag()); + } + + @Specialization + static PComplex doDouble(double left, PComplex right, + @Bind PythonLanguage language) { + return PFactory.createComplex(language, left + right.getReal(), 0.0 + right.getImag()); } @Specialization @@ -1077,13 +1110,25 @@ static ComplexValue multiply(ComplexValue left, ComplexValue right) { abstract static class SubNode extends BinaryOpBuiltinNode { static PComplex doComplex(PComplex left, double right, @Bind PythonLanguage language) { - return PFactory.createComplex(language, left.getReal() - right, left.getImag()); + return PFactory.createComplex(language, left.getReal() - right, left.getImag() - 0.0); } @Specialization static PComplex doComplex(PComplex left, int right, @Bind PythonLanguage language) { - return PFactory.createComplex(language, left.getReal() - right, left.getImag()); + return PFactory.createComplex(language, left.getReal() - right, left.getImag() - 0.0); + } + + @Specialization + static PComplex doComplex(int left, PComplex right, + @Bind PythonLanguage language) { + return PFactory.createComplex(language, left - right.getReal(), 0.0 - right.getImag()); + } + + @Specialization + static PComplex doComplex(double left, PComplex right, + @Bind PythonLanguage language) { + return PFactory.createComplex(language, left - right.getReal(), 0.0 - right.getImag()); } @Specialization @@ -1383,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); } } @@ -1399,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/deque/DequeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/deque/DequeBuiltins.java index 2e35ba8f06..57e83e896b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/deque/DequeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/deque/DequeBuiltins.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 @@ -76,7 +76,6 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; -import com.oracle.graal.python.builtins.objects.common.IndexNodes.NormalizeIndexCustomMessageNode; import com.oracle.graal.python.builtins.objects.deque.DequeBuiltinsClinicProviders.DequeInsertNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.deque.DequeBuiltinsClinicProviders.DequeRotateNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.function.PKeyword; @@ -824,9 +823,12 @@ public abstract static class DequeGetItemNode extends SqItemBuiltinNode { @Specialization @TruffleBoundary Object doGeneric(PDeque self, int idx, - @Cached NormalizeIndexCustomMessageNode normalizeIndexNode) { - int normIdx = normalizeIndexNode.execute(idx, self.getSize(), ErrorMessages.DEQUE_INDEX_OUT_OF_RANGE); - return doGetItem(self, normIdx); + @Bind Node inliningTarget, + @Cached PRaiseNode raiseNode) { + if (idx < 0 || idx >= self.getSize()) { + throw raiseNode.raise(inliningTarget, IndexError, ErrorMessages.DEQUE_INDEX_OUT_OF_RANGE); + } + return doGetItem(self, idx); } @TruffleBoundary @@ -846,9 +848,12 @@ public abstract static class DequeSetItemNode extends SqAssItemBuiltinNode { @Specialization static void setOrDel(PDeque self, int idx, Object value, - @Cached NormalizeIndexCustomMessageNode normalizeIndexNode) { - int normIdx = normalizeIndexNode.execute(idx, self.getSize(), ErrorMessages.DEQUE_INDEX_OUT_OF_RANGE); - self.setItem(normIdx, value != PNone.NO_VALUE ? value : null); + @Bind Node inliningTarget, + @Cached PRaiseNode raiseNode) { + if (idx < 0 || idx >= self.getSize()) { + throw raiseNode.raise(inliningTarget, IndexError, ErrorMessages.DEQUE_INDEX_OUT_OF_RANGE); + } + self.setItem(idx, value != PNone.NO_VALUE ? value : null); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DefaultDictBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DefaultDictBuiltins.java index 4bd39b2182..5c4d891d71 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DefaultDictBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DefaultDictBuiltins.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 @@ -78,6 +78,7 @@ import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.PythonUtils; +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.Fallback; @@ -149,13 +150,27 @@ static Object reduce(VirtualFrame frame, PDefaultDict self, @Builtin(name = "copy", minNumOfPositionalArgs = 1) @GenerateNodeFactory public abstract static class CopyNode extends PythonUnaryBuiltinNode { - @Specialization - static PDefaultDict copy(@SuppressWarnings("unused") VirtualFrame frame, PDefaultDict self, + @Specialization(guards = "isBuiltinDefaultDict(self)") + static PDefaultDict copyBuiltin(PDefaultDict self, @Bind Node inliningTarget, @Cached HashingStorageCopy copyNode, @Bind PythonLanguage language) { return PFactory.createDefaultDict(language, self.getDefaultFactory(), copyNode.execute(inliningTarget, self.getDictStorage())); } + + @Fallback + @InliningCutoff + static Object copyGeneric(VirtualFrame frame, Object self, + @Bind Node inliningTarget, + @Cached GetClassNode getClassNode, + @Cached CallNode callNode) { + PDefaultDict defaultDict = (PDefaultDict) self; + return callNode.execute(frame, getClassNode.execute(inliningTarget, defaultDict), defaultDict.getDefaultFactory(), defaultDict); + } + + static boolean isBuiltinDefaultDict(PDefaultDict self) { + return self.getPythonClass() == PythonBuiltinClassType.PDefaultDict; + } } @Builtin(name = J___MISSING__, minNumOfPositionalArgs = 2) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictBuiltins.java index 6a0c3b1dbd..20172f498f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictBuiltins.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. @@ -157,7 +157,6 @@ static Object dict(Object cls, Object[] args, PKeyword[] keywordArgs, @Bind Node inliningTarget, @Cached InlinedConditionProfile orderedProfile, @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, - @Cached CExtNodes.DictSubtypeNew subtypeNew, @Cached IsSubtypeNode isSubtypeNode, @Cached TypeNodes.GetInstanceShape getInstanceShape) { Shape shape = getInstanceShape.execute(cls); @@ -166,7 +165,7 @@ static Object dict(Object cls, Object[] args, PKeyword[] keywordArgs, } PDict newDict = PFactory.createDict(cls, shape, EmptyStorage.INSTANCE); if (needsNativeAllocationNode.execute(inliningTarget, cls)) { - return subtypeNew.execute(inliningTarget, cls, newDict); + return CExtNodes.allocateNativePart(inliningTarget, cls, newDict); } else { return newDict; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictNodes.java index 955902237b..be19c58492 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictNodes.java @@ -131,6 +131,7 @@ static void doForeign(Object dict, HashingStorage oldStorage, HashingStorage new } @GenerateInline(false) // footprint reduction 52 -> 36 + @GenerateUncached public abstract static class UpdateNode extends PNodeWithContext { public abstract void execute(Frame frame, Object self, Object other); @@ -155,6 +156,7 @@ public static UpdateNode create() { } @GenerateInline + @GenerateUncached @GenerateCached(false) public abstract static class UpdateInnerNode extends PNodeWithContext { public abstract void execute(Frame frame, Node inliningTarget, Object self, HashingStorage selfStorage, Object other, Object otherStorage); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictReprBuiltin.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictReprBuiltin.java index 177943346b..e17d1934a0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictReprBuiltin.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictReprBuiltin.java @@ -41,7 +41,6 @@ package com.oracle.graal.python.builtins.objects.dict; import static com.oracle.graal.python.nodes.SpecialMethodNames.T_ITEMS; -import static com.oracle.graal.python.nodes.SpecialMethodNames.T___REPR__; import static com.oracle.graal.python.nodes.StringLiterals.T_COLON_SPACE; import static com.oracle.graal.python.nodes.StringLiterals.T_COMMA_SPACE; import static com.oracle.graal.python.nodes.StringLiterals.T_ELLIPSIS; @@ -78,17 +77,12 @@ import com.oracle.graal.python.lib.PyIterNextNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetIter; -import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; import com.oracle.graal.python.nodes.PGuards; -import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.LookupAndCallUnaryDynamicNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; -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.exception.PythonErrorType; import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.ValueType; @@ -163,17 +157,12 @@ protected final int getLimit() { @Override public abstract ReprState execute(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, ReprState s); - protected static TruffleString getReprString(Node inliningTarget, Object obj, ReprState s, - LookupAndCallUnaryDynamicNode reprNode, - CastToTruffleStringNode castStr, - PRaiseNode raiseNode) { - TruffleString ellipsisStr = s == null || s.ellipsisInBraces ? T_ELLIPSIS_IN_BRACES : T_ELLIPSIS; - Object reprObj = s == null || obj != s.self ? reprNode.executeObject(obj, T___REPR__) : ellipsisStr; - try { - return castStr.execute(inliningTarget, reprObj); - } catch (CannotCastException e) { - throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.RETURNED_NON_STRING, "__repr__", reprObj); + protected static TruffleString getReprString(Frame frame, Node inliningTarget, Object obj, ReprState s, + PyObjectReprAsTruffleStringNode reprNode) { + if (s != null && obj == s.self) { + return s.ellipsisInBraces ? T_ELLIPSIS_IN_BRACES : T_ELLIPSIS; } + return reprNode.execute(frame, inliningTarget, obj); } protected static void appendSeparator(Node inliningTarget, ReprState s, InlinedConditionProfile lengthCheck, TruffleStringBuilder.AppendStringNode appendStringNode) { @@ -191,15 +180,13 @@ public ForEachKeyRepr(int limit) { @Specialization public static ReprState append(@SuppressWarnings("unused") Node node, HashingStorage storage, HashingStorageIterator it, ReprState s, @Bind Node inliningTarget, - @Cached LookupAndCallUnaryDynamicNode reprNode, - @Cached CastToTruffleStringNode castStr, - @Cached PRaiseNode raiseNode, + @Cached PyObjectReprAsTruffleStringNode reprNode, @Cached InlinedConditionProfile lengthCheck, @Cached HashingStorageIteratorKey itKey, @Cached TruffleStringBuilder.AppendStringNode appendStringNode) { appendSeparator(inliningTarget, s, lengthCheck, appendStringNode); Object key = itKey.execute(inliningTarget, storage, it); - appendStringNode.execute(s.result, getReprString(inliningTarget, key, null, reprNode, castStr, raiseNode)); + appendStringNode.execute(s.result, getReprString(null, inliningTarget, key, null, reprNode)); return s; } } @@ -212,15 +199,13 @@ public ForEachValueRepr(int limit) { @Specialization public static ReprState dict(Frame frame, @SuppressWarnings("unused") Node node, HashingStorage storage, HashingStorageIterator it, ReprState s, @Bind Node inliningTarget, - @Cached LookupAndCallUnaryDynamicNode reprNode, - @Cached CastToTruffleStringNode castStr, - @Cached PRaiseNode raiseNode, + @Cached PyObjectReprAsTruffleStringNode reprNode, @Cached InlinedConditionProfile lengthCheck, @Cached HashingStorageIteratorValue itValue, @Cached TruffleStringBuilder.AppendStringNode appendStringNode) { appendSeparator(inliningTarget, s, lengthCheck, appendStringNode); Object value = itValue.execute(inliningTarget, storage, it); - appendStringNode.execute(s.result, getReprString(inliningTarget, value, s, reprNode, castStr, raiseNode)); + appendStringNode.execute(s.result, getReprString(frame, inliningTarget, value, s, reprNode)); return s; } } @@ -233,10 +218,8 @@ public ForEachItemRepr(int limit) { @Specialization public static ReprState dict(Frame frame, @SuppressWarnings("unused") Node node, HashingStorage storage, HashingStorageIterator it, ReprState s, @Bind Node inliningTarget, - @Cached LookupAndCallUnaryDynamicNode keyReprNode, - @Cached LookupAndCallUnaryDynamicNode valueReprNode, - @Cached CastToTruffleStringNode castStr, - @Cached PRaiseNode raiseNode, + @Cached PyObjectReprAsTruffleStringNode keyReprNode, + @Cached PyObjectReprAsTruffleStringNode valueReprNode, @Cached InlinedConditionProfile lengthCheck, @Cached HashingStorageIteratorKey itKey, @Cached HashingStorageIteratorValue itValue, @@ -245,9 +228,9 @@ public static ReprState dict(Frame frame, @SuppressWarnings("unused") Node node, appendStringNode.execute(s.result, T_LPAREN); Object key = itKey.execute(inliningTarget, storage, it); Object value = itValue.execute(inliningTarget, storage, it); - appendStringNode.execute(s.result, getReprString(inliningTarget, key, null, keyReprNode, castStr, raiseNode)); + appendStringNode.execute(s.result, getReprString(frame, inliningTarget, key, null, keyReprNode)); appendStringNode.execute(s.result, T_COMMA_SPACE); - appendStringNode.execute(s.result, getReprString(inliningTarget, value, s, valueReprNode, castStr, raiseNode)); + appendStringNode.execute(s.result, getReprString(frame, inliningTarget, value, s, valueReprNode)); appendStringNode.execute(s.result, T_RPAREN); return s; } @@ -261,18 +244,16 @@ public ForEachDictRepr(int limit) { @Specialization public static ReprState dict(Frame frame, @SuppressWarnings("unused") Node node, HashingStorage storage, HashingStorageIterator it, ReprState s, @Bind Node inliningTarget, - @Cached LookupAndCallUnaryDynamicNode keyReprNode, - @Cached LookupAndCallUnaryDynamicNode valueReprNode, - @Cached CastToTruffleStringNode castStr, - @Cached PRaiseNode raiseNode, + @Cached PyObjectReprAsTruffleStringNode keyReprNode, + @Cached PyObjectReprAsTruffleStringNode valueReprNode, @Cached InlinedConditionProfile lengthCheck, @Cached HashingStorageIteratorKey itKey, @Cached HashingStorageIteratorValue itValue, @Cached TruffleStringBuilder.AppendStringNode appendStringNode) { Object key = itKey.execute(inliningTarget, storage, it); Object value = itValue.execute(inliningTarget, storage, it); - TruffleString keyReprString = getReprString(inliningTarget, key, null, keyReprNode, castStr, raiseNode); - TruffleString valueReprString = getReprString(inliningTarget, value, s, valueReprNode, castStr, raiseNode); + TruffleString keyReprString = getReprString(frame, inliningTarget, key, null, keyReprNode); + TruffleString valueReprString = getReprString(frame, inliningTarget, value, s, valueReprNode); appendSeparator(inliningTarget, s, lengthCheck, appendStringNode); appendStringNode.execute(s.result, keyReprString); appendStringNode.execute(s.result, T_COLON_SPACE); @@ -379,14 +360,12 @@ public abstract static class FormatKeyValueDictRepr extends Node { @Specialization public static void keyValue(Object key, Object value, ReprState s, @Bind Node inliningTarget, - @Cached LookupAndCallUnaryDynamicNode keyReprNode, - @Cached LookupAndCallUnaryDynamicNode valueReprNode, - @Cached CastToTruffleStringNode castStr, - @Cached PRaiseNode raiseNode, + @Cached PyObjectReprAsTruffleStringNode keyReprNode, + @Cached PyObjectReprAsTruffleStringNode valueReprNode, @Cached InlinedConditionProfile lengthCheck, @Cached TruffleStringBuilder.AppendStringNode appendStringNode) { - TruffleString keyReprString = AbstractForEachRepr.getReprString(inliningTarget, key, null, keyReprNode, castStr, raiseNode); - TruffleString valueReprString = AbstractForEachRepr.getReprString(inliningTarget, value, s, valueReprNode, castStr, raiseNode); + TruffleString keyReprString = AbstractForEachRepr.getReprString(null, inliningTarget, key, null, keyReprNode); + TruffleString valueReprString = AbstractForEachRepr.getReprString(null, inliningTarget, value, s, valueReprNode); AbstractForEachRepr.appendSeparator(inliningTarget, s, lengthCheck, appendStringNode); appendStringNode.execute(s.result, keyReprString); appendStringNode.execute(s.result, T_COLON_SPACE); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java index a82ffa0a3a..74557d6a7d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/dict/DictViewBuiltins.java @@ -52,17 +52,31 @@ import com.oracle.graal.python.builtins.CoreFunctions; 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.PNotImplemented; +import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; +import com.oracle.graal.python.builtins.objects.common.EmptyStorage; +import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.GetSetStorageForXorNode; 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.common.HashingStorageNodes.HashingStorageCopy; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageDiff; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageForEach; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageForEachCallback; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItemWithHash; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetIterator; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetReverseIterator; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIntersect; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIterator; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKey; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKeyHash; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorValue; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageLen; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageTransferItem; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageXor; +import com.oracle.graal.python.builtins.objects.common.ObjectHashMap; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.dict.DictViewBuiltinsFactory.ContainedInNodeGen; import com.oracle.graal.python.builtins.objects.dict.PDictView.PDictItemsView; @@ -84,6 +98,7 @@ import com.oracle.graal.python.lib.PyObjectSizeNode; import com.oracle.graal.python.lib.PySequenceContainsNode; import com.oracle.graal.python.lib.RichCmpOp; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -100,6 +115,7 @@ import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateNodeFactory; 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.Specialization; @@ -442,7 +458,7 @@ static PBaseSet doItemsView(VirtualFrame frame, PDictItemsView self, PBaseSet ot return PFactory.createSet(language, storage); } - @Specialization + @Fallback static PBaseSet doGeneric(VirtualFrame frame, Object self, Object other, @Bind Node inliningTarget, @Shared("constrSet") @Cached SetNodes.ConstructSetNode constructSetNode, @@ -533,19 +549,137 @@ static PBaseSet doGeneric(VirtualFrame frame, Object self, Object other, } } + static final class DictItemsXorState { + final PythonLanguage language; + final EconomicMapStorage remaining; + HashingStorage resultStorage = EmptyStorage.INSTANCE; + + DictItemsXorState(PythonLanguage language, EconomicMapStorage remaining) { + this.language = language; + this.remaining = remaining; + } + } + + @GenerateInline + @GenerateCached(false) + abstract static class CopyToEconomicMapNode extends Node { + abstract EconomicMapStorage execute(Frame frame, Node inliningTarget, HashingStorage storage); + + @Specialization + static EconomicMapStorage doEconomic(Node inliningTarget, EconomicMapStorage storage, + @Cached HashingStorageCopy copyNode) { + return (EconomicMapStorage) copyNode.execute(inliningTarget, storage); + } + + @Specialization(replaces = "doEconomic") + static EconomicMapStorage doGeneric(Frame frame, Node inliningTarget, HashingStorage storage, + @Cached HashingStorageForEach forEach, + @Cached HashingStorageTransferItem transferItem) { + EconomicMapStorage result = EconomicMapStorage.createWithSideEffects(); + HashingStorage copied = forEach.execute(frame, inliningTarget, storage, transferItem, result); + assert copied == result; + return result; + } + } + + @GenerateInline + @GenerateCached(false) + public abstract static class DictItemsXorCallback extends HashingStorageForEachCallback { + @Override + public abstract DictItemsXorState execute(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, DictItemsXorState state); + + @Specialization + static DictItemsXorState doGeneric(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, DictItemsXorState state, + @Cached HashingStorageGetItemWithHash getItem, + @Cached ObjectHashMap.RemoveNode removeNode, + @Cached PyObjectRichCompareBool eqNode, + @Cached HashingStorageSetItem setItem, + @Cached HashingStorageIteratorKey iterKey, + @Cached HashingStorageIteratorValue iterValue, + @Cached HashingStorageIteratorKeyHash iterHash) { + Object key = iterKey.execute(inliningTarget, storage, it); + long hash = iterHash.execute(frame, inliningTarget, storage, it); + Object rightValue = iterValue.execute(inliningTarget, storage, it); + Object leftValue = getItem.execute(frame, inliningTarget, state.remaining, key, hash); + boolean delete = leftValue != null && eqNode.execute(frame, inliningTarget, leftValue, rightValue, RichCmpOp.Py_EQ); + if (delete) { + removeNode.execute(frame, inliningTarget, state.remaining, key, hash); + } else { + PTuple pair = PFactory.createTuple(state.language, new Object[]{key, rightValue}); + state.resultStorage = setItem.execute(frame, inliningTarget, state.resultStorage, pair, PNone.NONE); + } + return state; + } + } + + @GenerateInline + @GenerateCached(false) + public abstract static class AddDictItemsToSetCallback extends HashingStorageForEachCallback { + @Override + public abstract HashingStorage execute(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, HashingStorage resultStorage); + + @Specialization + static HashingStorage doGeneric(Frame frame, Node inliningTarget, HashingStorage storage, HashingStorageIterator it, HashingStorage resultStorage, + @Bind PythonLanguage language, + @Cached HashingStorageSetItem setItem, + @Cached HashingStorageIteratorKey iterKey, + @Cached HashingStorageIteratorValue iterValue) { + PTuple pair = PFactory.createTuple(language, new Object[]{iterKey.execute(inliningTarget, storage, it), iterValue.execute(inliningTarget, storage, it)}); + return setItem.execute(frame, inliningTarget, resultStorage, pair, PNone.NONE); + } + } + @Slot(value = SlotKind.nb_xor, isComplex = true) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class XorNode extends BinaryOpBuiltinNode { @Specialization + static PBaseSet doItemsViews(VirtualFrame frame, PDictItemsView self, PDictItemsView other, + @Bind Node inliningTarget, + @Cached CopyToEconomicMapNode copyToEconomicMapNode, + @Cached HashingStorageForEach forEachRight, + @Cached HashingStorageForEach forEachRemaining, + @Cached DictItemsXorCallback xorCallback, + @Cached AddDictItemsToSetCallback addRemainingCallback, + @Bind PythonLanguage language) { + // CPython has a dedicated dictitems_xor helper: copy the left dict, iterate the right + // dict by key/hash, compare values for matching keys, and emit item tuples. + EconomicMapStorage remaining = copyToEconomicMapNode.execute(frame, inliningTarget, self.getWrappedStorage()); + DictItemsXorState state = new DictItemsXorState(language, remaining); + forEachRight.execute(frame, inliningTarget, other.getWrappedStorage(), xorCallback, state); + HashingStorage result = forEachRemaining.execute(frame, inliningTarget, remaining, addRemainingCallback, state.resultStorage); + return PFactory.createSet(language, result); + } + + @Specialization(guards = "isDictKeysView(self) || isAnySet(self)") + static PBaseSet doKeysViewOrSet(VirtualFrame frame, Object self, Object other, + @Bind Node inliningTarget, + @Shared @Cached GetSetStorageForXorNode getRightStorage, + @Shared @Cached HashingStorageXor xor, + @Bind PythonLanguage language) { + HashingStorage left = getKeysViewOrSetStorage(self); + HashingStorage right = getRightStorage.execute(frame, inliningTarget, other); + return PFactory.createSet(language, xor.executePreservingLeft(frame, inliningTarget, left, right)); + } + + private static HashingStorage getKeysViewOrSetStorage(Object self) { + if (self instanceof PDictKeysView keysView) { + return keysView.getWrappedStorage(); + } + return ((PBaseSet) self).getDictStorage(); + } + + @Fallback static PBaseSet doGeneric(VirtualFrame frame, Object self, Object other, @Bind Node inliningTarget, - @Cached GetStorageForBinopNode getStorage, - @Cached HashingStorageXor xor, + @Cached SetNodes.ConstructSetNode constructSetNode, + @Shared @Cached GetSetStorageForXorNode getRightStorage, + @Shared @Cached HashingStorageXor xor, @Bind PythonLanguage language) { - HashingStorage left = getStorage.execute(frame, inliningTarget, self); - HashingStorage right = getStorage.execute(frame, inliningTarget, other); - return PFactory.createSet(language, xor.execute(frame, inliningTarget, left, right)); + HashingStorage left = constructSetNode.execute(frame, self).getDictStorage(); + HashingStorage right = getRightStorage.execute(frame, inliningTarget, other); + return PFactory.createSet(language, xor.executeMutatingLeft(frame, inliningTarget, left, right)); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionAttrNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionAttrNode.java index ce7d55cedc..783c8e2a4e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionAttrNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionAttrNode.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 @@ -75,6 +75,12 @@ public final int getInt(PBaseException self, int index, StorageFactory factory) return (int) val; } + public final long getLong(PBaseException self, int index, StorageFactory factory) { + final Object val = execute(self, PNone.NO_VALUE, index, factory); + assert val instanceof Integer || val instanceof Long : "expected PBaseException attribute to be an integer"; + return val instanceof Integer ? (int) val : (long) val; + } + public final Object set(PBaseException self, Object value, int index, StorageFactory factory) { return execute(self, value, index, factory); } 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..48c1823533 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, @@ -154,16 +160,27 @@ static Object doManaged(Object cls, @SuppressWarnings("unused") Object[] args, @ @Specialization(guards = "needsNativeAllocationNode.execute(inliningTarget, cls)", limit = "1") static Object doNativeSubtype(Object cls, Object[] args, @SuppressWarnings("unused") PKeyword[] kwargs, - @SuppressWarnings("unused") @Bind Node inliningTarget, + @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 CApiTransitions.PythonToNativeInternalNode toNativeNode, + @Cached CApiTransitions.NativeToPythonInternalNode toPythonNode, + @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.execute(inliningTarget, cls); + long argsTuplePointer = toNativeNode.execute(inliningTarget, 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.executeTransfer(inliningTarget, nativeResult)); + } finally { + Reference.reachabilityFence(cls); + Reference.reachabilityFence(argsTuple); + } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionGroupBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionGroupBuiltins.java index 43b16365cc..ca874691b0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionGroupBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionGroupBuiltins.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,7 +51,10 @@ import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Set; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; @@ -63,6 +66,7 @@ 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.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.dict.PDict; @@ -299,6 +303,44 @@ private static PBaseExceptionGroup subset(Node inliningTarget, PBaseExceptionGro return eg; } + /** + * Build the sub-exception group of {@code orig} that contains all leaf exceptions also present + * in {@code keep}. This corresponds to CPython's {@code exception_group_projection()} helper. + */ + @TruffleBoundary + public static PBaseExceptionGroup exceptionGroupProjection(Node inliningTarget, PBaseExceptionGroup orig, Object[] keep) { + Set leafExceptions = Collections.newSetFromMap(new IdentityHashMap<>()); + for (Object exception : keep) { + collectExceptionGroupLeafIdentities(exception, leafExceptions); + } + return exceptionGroupProjection(inliningTarget, orig, leafExceptions); + } + + private static void collectExceptionGroupLeafIdentities(Object exception, Set leafExceptions) { + if (exception instanceof PBaseExceptionGroup group) { + for (Object child : group.getExceptions()) { + collectExceptionGroupLeafIdentities(child, leafExceptions); + } + } else if (exception != PNone.NONE) { + leafExceptions.add(exception); + } + } + + private static PBaseExceptionGroup exceptionGroupProjection(Node inliningTarget, PBaseExceptionGroup orig, Set leafExceptions) { + List matches = new ArrayList<>(); + for (Object exception : orig.getExceptions()) { + if (exception instanceof PBaseExceptionGroup group) { + PBaseExceptionGroup projected = exceptionGroupProjection(inliningTarget, group, leafExceptions); + if (projected != null) { + matches.add(projected); + } + } else if (leafExceptions.contains(exception)) { + matches.add(exception); + } + } + return subset(inliningTarget, orig, matches.toArray()); + } + private enum MatcherType { BY_TYPE, BY_PREDICATE, @@ -314,8 +356,8 @@ private static MatcherType getMatcherType(Node inliningTarget, Object value) { if (isExceptionTypeUncached(value)) { return MatcherType.BY_TYPE; } - if (value instanceof PTuple tuple && PyTupleCheckExactNode.executeUncached(tuple)) { - SequenceStorage storage = tuple.getSequenceStorage(); + SequenceStorage storage = getExactTupleStorage(value); + if (storage != null) { for (int i = 0; i < storage.length(); i++) { Object elem = SequenceStorageNodes.GetItemScalarNode.executeUncached(storage, i); if (!isExceptionTypeUncached(elem)) { @@ -327,6 +369,16 @@ private static MatcherType getMatcherType(Node inliningTarget, Object value) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.EXPECTED_A_FUNCTION_EXCEPTION_TYPE_OR_TUPLE_OF_EXCEPTION_TYPES); } + private static SequenceStorage getExactTupleStorage(Object value) { + if (value instanceof PTuple tuple && PyTupleCheckExactNode.executeUncached(tuple)) { + return tuple.getSequenceStorage(); + } + if (value instanceof PythonAbstractNativeObject nativeTuple && PyTupleCheckExactNode.executeUncached(nativeTuple)) { + return TupleNodes.GetNativeTupleStorage.getUncached().execute(nativeTuple); + } + return null; + } + private static boolean isExceptionTypeUncached(Object value) { return TypeNodes.IsTypeNode.executeUncached(value) && BuiltinClassProfiles.IsBuiltinClassProfile.profileClassSlowPath(value, PythonBuiltinClassType.PBaseException); } 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/exception/PBaseException.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PBaseException.java index aeb261bc99..faf2ca6e8a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PBaseException.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PBaseException.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 @@ -360,7 +360,7 @@ int getExceptionExitStatus( @Cached CastToJavaIntExactNode castToInt, @Bind Node inliningTarget, @Exclusive @Cached GetClassNode getClassNode, - @Cached ReadAttributeFromPythonObjectNode readNode, + @Cached(inline = true) ReadAttributeFromPythonObjectNode readNode, @Exclusive @Cached InlinedBranchProfile unsupportedProfile, @Shared("gil") @Cached GilNode gil) throws UnsupportedMessageException { boolean mustRelease = gil.acquire(); @@ -368,7 +368,7 @@ int getExceptionExitStatus( if (getExceptionType(inliningTarget, getClassNode, gil) == ExceptionType.EXIT) { try { // Avoiding getattr because this message shouldn't have side-effects - Object code = readNode.execute(this, T_CODE); + Object code = readNode.execute(inliningTarget, this, T_CODE); if (code == PNone.NO_VALUE) { return 1; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PrepareExceptionNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PrepareExceptionNode.java index 64891dd61f..dd31b7aca9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PrepareExceptionNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PrepareExceptionNode.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 @@ -44,16 +44,17 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; -import com.oracle.graal.python.builtins.objects.common.SequenceNodes; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode; import com.oracle.graal.python.lib.PyExceptionInstanceCheckNode; +import com.oracle.graal.python.lib.PyObjectIsInstanceNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; 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.builtins.TupleNodes.GetTupleStorage; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.runtime.object.PFactory; @@ -96,18 +97,19 @@ static Object doException(@SuppressWarnings("unused") PBaseException exc, @Suppr throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.INSTANCE_EX_MAY_NOT_HAVE_SEP_VALUE); } - @Specialization(guards = {"isTypeNode.execute(inliningTarget, type)", "!isPNone(value)", "!isPTuple(value)"}, limit = "1") + @Specialization(guards = {"isTypeNode.execute(inliningTarget, type)", "!isPNone(value)", "!tupleCheck.execute(inliningTarget, value)"}, limit = "1") static Object doExceptionOrCreate(VirtualFrame frame, Object type, Object value, @Bind Node inliningTarget, @SuppressWarnings("unused") @Exclusive @Cached IsTypeNode isTypeNode, @Exclusive @Cached PyExceptionInstanceCheckNode check, - @Cached BuiltinFunctions.IsInstanceNode isInstanceNode, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck, + @Cached PyObjectIsInstanceNode isInstanceNode, @Cached InlinedConditionProfile isInstanceProfile, @Shared @Cached IsSubtypeNode isSubtypeNode, @Exclusive @Cached PRaiseNode raiseNode, @Shared("callCtor") @Cached CallNode callConstructor) { checkExceptionClass(inliningTarget, type, isSubtypeNode, raiseNode); - if (isInstanceProfile.profile(inliningTarget, isInstanceNode.executeWith(frame, value, type))) { + if (isInstanceProfile.profile(inliningTarget, isInstanceNode.execute(frame, value, type))) { return value; } else { Object instance = callConstructor.execute(frame, type, value); @@ -136,17 +138,19 @@ static Object doCreate(VirtualFrame frame, Object type, @SuppressWarnings("unuse } } - @Specialization(guards = "isTypeNode.execute(inliningTarget, type)", limit = "1") - static Object doCreateTuple(VirtualFrame frame, Object type, PTuple value, + @Specialization(guards = {"isTypeNode.execute(inliningTarget, type)", "tupleCheck.execute(inliningTarget, value)"}, limit = "1") + static Object doCreateTuple(VirtualFrame frame, Object type, Object value, @Bind Node inliningTarget, @SuppressWarnings("unused") @Exclusive @Cached IsTypeNode isTypeNode, @Exclusive @Cached PyExceptionInstanceCheckNode check, - @Cached SequenceNodes.GetObjectArrayNode getObjectArrayNode, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck, + @Exclusive @Cached GetTupleStorage getTupleStorage, + @Exclusive @Cached SequenceStorageNodes.ToArrayNode toArrayNode, @Shared @Cached IsSubtypeNode isSubtypeNode, @Exclusive @Cached PRaiseNode raiseNode, @Shared("callCtor") @Cached CallNode callConstructor) { checkExceptionClass(inliningTarget, type, isSubtypeNode, raiseNode); - Object[] args = getObjectArrayNode.execute(inliningTarget, value); + Object[] args = toArrayNode.execute(inliningTarget, getTupleStorage.execute(inliningTarget, value)); Object instance = callConstructor.execute(frame, type, args); if (check.execute(inliningTarget, instance)) { return instance; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/StopIterationBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/StopIterationBuiltins.java index d12f45e0bd..7613409bda 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/StopIterationBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/StopIterationBuiltins.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 @@ -76,7 +76,7 @@ protected List> getNodeFa @Slot(value = SlotKind.tp_init, isComplex = true) @SlotSignature(minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true) @GenerateNodeFactory - public abstract static class StopIterationInitNode extends PythonVarargsBuiltinNode { + abstract static class StopIterationInitNode extends PythonVarargsBuiltinNode { @Specialization static Object init(VirtualFrame frame, PBaseException self, Object[] args, PKeyword[] keywords, @@ -89,7 +89,7 @@ static Object init(VirtualFrame frame, PBaseException self, Object[] args, PKeyw @Builtin(name = "value", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "generator return value") @GenerateNodeFactory - public abstract static class StopIterationValueNode extends PythonBinaryBuiltinNode { + abstract static class StopIterationValueNode extends PythonBinaryBuiltinNode { public final Object execute(PBaseException self) { return execute(null, self, PNone.NO_VALUE); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/SyntaxErrorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/SyntaxErrorBuiltins.java index af78861433..e20e962e9b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/SyntaxErrorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/SyntaxErrorBuiltins.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,7 +43,7 @@ import static com.oracle.graal.python.nodes.ErrorMessages.MISSING_PARENTHESES_IN_CALL_TO_EXEC; import static com.oracle.graal.python.nodes.ErrorMessages.MISSING_PARENTHESES_IN_CALL_TO_PRINT; import static com.oracle.graal.python.nodes.ErrorMessages.TUPLE_OUT_OF_BOUNDS; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import java.util.List; @@ -55,6 +55,9 @@ 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.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.function.PKeyword; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; @@ -70,12 +73,12 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.util.CastToJavaStringNode; -import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.graal.python.util.OverflowException; 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.GenerateCached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; @@ -98,6 +101,16 @@ public final class SyntaxErrorBuiltins extends PythonBuiltins { public static final BaseExceptionAttrNode.StorageFactory SYNTAX_ERROR_ATTR_FACTORY = (args) -> new Object[SYNTAX_ERR_NUM_ATTRS]; + private static final CFields[] NATIVE_ATTR_FIELDS = { + CFields.PySyntaxErrorObject__msg, + CFields.PySyntaxErrorObject__filename, + CFields.PySyntaxErrorObject__lineno, + CFields.PySyntaxErrorObject__offset, + CFields.PySyntaxErrorObject__text, + CFields.PySyntaxErrorObject__end_lineno, + CFields.PySyntaxErrorObject__end_offset, + CFields.PySyntaxErrorObject__print_file_and_line}; + public static final TpSlots SLOTS = SyntaxErrorBuiltinsSlotsGen.SLOTS; @Override @@ -105,9 +118,15 @@ protected List> getNodeFa return SyntaxErrorBuiltinsFactory.getFactories(); } + private static Object getNativeAttr(PythonAbstractNativeObject self, int index, CStructAccess.ReadObjectNode readObjectNode) { + Object result = readObjectNode.readFromObj(self, NATIVE_ATTR_FIELDS[index]); + return result == PNone.NO_VALUE ? PNone.NONE : result; + } + @Slot(value = SlotKind.tp_init, isComplex = true) @SlotSignature(minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true) @GenerateNodeFactory + @SuppressWarnings({"truffle-sharing", "truffle-unused"}) public abstract static class SyntaxErrorInitNode extends PythonBuiltinNode { private static final String PREFIX_PRINT = "print "; private static final String PREFIX_EXEC = "exec "; @@ -187,15 +206,11 @@ private static String trimLeft(String str, int start) { return str.substring(st); } - @Specialization - static Object init(VirtualFrame frame, PBaseException self, Object[] args, PKeyword[] keywords, - @Bind Node inliningTarget, - @Cached CastToJavaStringNode castToJavaStringNode, - @Cached TupleNodes.ConstructTupleNode constructTupleNode, - @Cached SequenceStorageNodes.GetItemNode getItemNode, - @Cached BaseExceptionBuiltins.BaseExceptionInitNode baseExceptionInitNode, - @Cached PRaiseNode raiseNode) { - baseExceptionInitNode.execute(frame, self, args, keywords); + private static Object[] initAttrs(VirtualFrame frame, Object[] args, Node inliningTarget, + CastToJavaStringNode castToJavaStringNode, + TupleNodes.ConstructTupleNode constructTupleNode, + SequenceStorageNodes.GetItemNode getItemNode, + PRaiseNode raiseNode) { Object[] attrs = SYNTAX_ERROR_ATTR_FACTORY.create(); if (args.length >= 1) { attrs[IDX_MSG] = args[0]; @@ -203,7 +218,8 @@ static Object init(VirtualFrame frame, PBaseException self, Object[] args, PKeyw if (args.length == 2) { PTuple info = constructTupleNode.execute(frame, args[1]); final SequenceStorage storage = info.getSequenceStorage(); - if (storage.length() != 4) { + int infoLength = storage.length(); + if (infoLength != 4 && infoLength != 6) { // not a very good error message, but it's what Python 2.4 gives throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.IndexError, TUPLE_OUT_OF_BOUNDS); } @@ -212,6 +228,10 @@ static Object init(VirtualFrame frame, PBaseException self, Object[] args, PKeyw attrs[IDX_LINENO] = getItemNode.execute(storage, 1); attrs[IDX_OFFSET] = getItemNode.execute(storage, 2); attrs[IDX_TEXT] = getItemNode.execute(storage, 3); + if (infoLength == 6) { + attrs[IDX_END_LINENO] = getItemNode.execute(storage, 4); + attrs[IDX_END_OFFSET] = getItemNode.execute(storage, 5); + } // Issue #21669: Custom error for 'print' & 'exec' as statements // Only applies to SyntaxError instances, not to subclasses such @@ -220,123 +240,175 @@ static Object init(VirtualFrame frame, PBaseException self, Object[] args, PKeyw attrs[IDX_MSG] = reportMissingParentheses(attrs[IDX_MSG], castToJavaStringNode.execute(attrs[IDX_TEXT])); } } - self.setExceptionAttributes(attrs); + return attrs; + } + + @Specialization + static Object init(VirtualFrame frame, PBaseException self, Object[] args, PKeyword[] keywords, + @Bind Node inliningTarget, + @Cached CastToJavaStringNode castToJavaStringNode, + @Cached TupleNodes.ConstructTupleNode constructTupleNode, + @Cached SequenceStorageNodes.GetItemNode getItemNode, + @Cached BaseExceptionBuiltins.BaseExceptionInitNode baseExceptionInitNode, + @Cached PRaiseNode raiseNode) { + baseExceptionInitNode.execute(frame, self, args, keywords); + self.setExceptionAttributes(initAttrs(frame, args, inliningTarget, castToJavaStringNode, constructTupleNode, getItemNode, raiseNode)); + return PNone.NONE; + } + + @Specialization + static Object initNative(VirtualFrame frame, PythonAbstractNativeObject self, Object[] args, PKeyword[] keywords, + @Bind Node inliningTarget, + @Cached CastToJavaStringNode castToJavaStringNode, + @Cached TupleNodes.ConstructTupleNode constructTupleNode, + @Cached SequenceStorageNodes.GetItemNode getItemNode, + @Cached BaseExceptionBuiltins.BaseExceptionInitNode baseExceptionInitNode, + @Cached PRaiseNode raiseNode, + @Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObjectNode) { + baseExceptionInitNode.execute(frame, self, args, keywords); + Object[] attrs = initAttrs(frame, args, inliningTarget, castToJavaStringNode, constructTupleNode, getItemNode, raiseNode); + for (int i = 0; i < SYNTAX_ERR_NUM_ATTRS; i++) { + Object attr = attrs[i]; + writeObjectNode.writeToObject(self, NATIVE_ATTR_FIELDS[i], attr == null ? PNone.NO_VALUE : attr); + } return PNone.NONE; } } - @Builtin(name = "msg", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception msg") - @GenerateNodeFactory - public abstract static class SyntaxErrorMsgNode extends PythonBuiltinNode { + @GenerateCached(false) // this avoids truffle generating a concrete subclass with a missing getIndex() + @SuppressWarnings({"truffle-static-method", "truffle-unused"}) + public abstract static class SyntaxErrorAttributeNode extends PythonBuiltinNode { + abstract int getIndex(); + @Specialization Object generic(PBaseException self, Object value, @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, value, IDX_MSG, SYNTAX_ERROR_ATTR_FACTORY); + return attrNode.execute(self, value, getIndex(), SYNTAX_ERROR_ATTR_FACTORY); + } + + @Specialization(guards = "isNoValue(value)") + Object getNative(PythonAbstractNativeObject self, @SuppressWarnings("unused") PNone value, + @Bind Node inliningTarget, + @Cached(inline = false) CStructAccess.ReadObjectNode readObjectNode) { + return getNativeAttr(self, getIndex(), readObjectNode); + } + + @Specialization(guards = "!isNoValue(valueIn)") + Object setNative(PythonAbstractNativeObject self, Object valueIn, + @Bind Node inliningTarget, + @Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObjectNode) { + Object value = PGuards.isDeleteMarker(valueIn) ? PNone.NO_VALUE : valueIn; + writeObjectNode.writeToObject(self, NATIVE_ATTR_FIELDS[getIndex()], value); + return PNone.NONE; + } + } + + @Builtin(name = "msg", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception msg") + @GenerateNodeFactory + public abstract static class SyntaxErrorMsgNode extends SyntaxErrorAttributeNode { + @Override + int getIndex() { + return IDX_MSG; } } @Builtin(name = "filename", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception filename") @GenerateNodeFactory - public abstract static class SyntaxErrorFilenameNode extends PythonBuiltinNode { - @Specialization - Object generic(PBaseException self, Object value, - @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, value, IDX_FILENAME, SYNTAX_ERROR_ATTR_FACTORY); + public abstract static class SyntaxErrorFilenameNode extends SyntaxErrorAttributeNode { + @Override + int getIndex() { + return IDX_FILENAME; } } @Builtin(name = "lineno", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception lineno") @GenerateNodeFactory - public abstract static class SyntaxErrorLinenoNode extends PythonBuiltinNode { - @Specialization - Object generic(PBaseException self, Object value, - @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, value, IDX_LINENO, SYNTAX_ERROR_ATTR_FACTORY); + public abstract static class SyntaxErrorLinenoNode extends SyntaxErrorAttributeNode { + @Override + int getIndex() { + return IDX_LINENO; } } @Builtin(name = "offset", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception offset") @GenerateNodeFactory - public abstract static class SyntaxErrorOffsetNode extends PythonBuiltinNode { - @Specialization - Object generic(PBaseException self, Object value, - @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, value, IDX_OFFSET, SYNTAX_ERROR_ATTR_FACTORY); + public abstract static class SyntaxErrorOffsetNode extends SyntaxErrorAttributeNode { + @Override + int getIndex() { + return IDX_OFFSET; } } @Builtin(name = "text", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception text") @GenerateNodeFactory - public abstract static class SyntaxErrorTextNode extends PythonBuiltinNode { - @Specialization - Object generic(PBaseException self, Object value, - @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, value, IDX_TEXT, SYNTAX_ERROR_ATTR_FACTORY); + public abstract static class SyntaxErrorTextNode extends SyntaxErrorAttributeNode { + @Override + int getIndex() { + return IDX_TEXT; } } @Builtin(name = "end_lineno", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception end lineno") @GenerateNodeFactory - public abstract static class SyntaxErrorEndLineNode extends PythonBuiltinNode { - @Specialization - Object generic(PBaseException self, Object value, - @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, value, IDX_END_LINENO, SYNTAX_ERROR_ATTR_FACTORY); + public abstract static class SyntaxErrorEndLineNode extends SyntaxErrorAttributeNode { + @Override + int getIndex() { + return IDX_END_LINENO; } } @Builtin(name = "end_offset", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception end offset") @GenerateNodeFactory - public abstract static class SyntaxErrorEndColumnNode extends PythonBuiltinNode { - @Specialization - Object generic(PBaseException self, Object value, - @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, value, IDX_END_OFFSET, SYNTAX_ERROR_ATTR_FACTORY); + public abstract static class SyntaxErrorEndColumnNode extends SyntaxErrorAttributeNode { + @Override + int getIndex() { + return IDX_END_OFFSET; } } @Builtin(name = "print_file_and_line", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, allowsDelete = true, doc = "exception print_file_and_line") @GenerateNodeFactory - public abstract static class SyntaxErrorPrintFileAndLineNode extends PythonBuiltinNode { - @Specialization - Object generic(PBaseException self, Object value, - @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, value, IDX_PRINT_FILE_AND_LINE, SYNTAX_ERROR_ATTR_FACTORY); + public abstract static class SyntaxErrorPrintFileAndLineNode extends SyntaxErrorAttributeNode { + @Override + int getIndex() { + return IDX_PRINT_FILE_AND_LINE; } } @Slot(value = SlotKind.tp_str, isComplex = true) @GenerateNodeFactory + @SuppressWarnings({"truffle-sharing", "truffle-static-method", "truffle-unused"}) public abstract static class SyntaxErrorStrNode extends PythonUnaryBuiltinNode { @Specialization TruffleString str(VirtualFrame frame, PBaseException self, @Bind Node inliningTarget, - @Cached BaseExceptionAttrNode attrNode, - @Cached PyObjectStrAsTruffleStringNode strNode, - @Cached CastToTruffleStringNode castToStringNode, - @Cached PyLongAsLongAndOverflowNode pyLongAsLongAndOverflowNode, - @Cached PyLongCheckExactNode pyLongCheckExactNode, - @Cached TruffleString.FromJavaStringNode fromJavaStringNode, - @Cached TruffleString.CodePointLengthNode codePointLengthNode, - @Cached TruffleString.LastIndexOfStringNode lastIndexOfStringNode, - @Cached TruffleString.SubstringNode substringNode, - @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { + @Cached BaseExceptionAttrNode attrNode) { + return formatStr(attrNode.get(self, IDX_FILENAME, SYNTAX_ERROR_ATTR_FACTORY), attrNode.get(self, IDX_LINENO, SYNTAX_ERROR_ATTR_FACTORY), attrNode.get(self, IDX_MSG, + SYNTAX_ERROR_ATTR_FACTORY)); + } + + @Specialization + TruffleString strNative(VirtualFrame frame, PythonAbstractNativeObject self, + @Bind Node inliningTarget, + @Cached CStructAccess.ReadObjectNode readObjectNode) { + return formatStr(getNativeAttr(self, IDX_FILENAME, readObjectNode), getNativeAttr(self, IDX_LINENO, readObjectNode), getNativeAttr(self, IDX_MSG, readObjectNode)); + } + + @TruffleBoundary + private TruffleString formatStr(Object filenameAttrValue, Object lineno, Object msg) { // Below, we always ignore overflow errors, just printing -1. // Still, we cannot allow an OverflowError to be raised, so // we need to call PyLong_AsLongAndOverflow. - TruffleString filename; - final Object filenameAttrValue = attrNode.get(self, IDX_FILENAME, SYNTAX_ERROR_ATTR_FACTORY); + String filename; if (filenameAttrValue != PNone.NONE && PGuards.isString(filenameAttrValue)) { - filename = castToStringNode.execute(inliningTarget, self.getExceptionAttribute(IDX_FILENAME)); - filename = getLastPathElement(filename, fromJavaStringNode, codePointLengthNode, lastIndexOfStringNode, substringNode); + filename = CastToJavaStringNode.getUncached().execute(filenameAttrValue); + filename = getLastPathElement(filename); } else { filename = null; } - final Object lineno = attrNode.get(self, IDX_LINENO, SYNTAX_ERROR_ATTR_FACTORY); - final Object msg = attrNode.get(self, IDX_MSG, SYNTAX_ERROR_ATTR_FACTORY); - boolean heaveLineNo = lineno != PNone.NONE && pyLongCheckExactNode.execute(inliningTarget, lineno); - final TruffleString msgStr = strNode.execute(frame, inliningTarget, msg); + boolean heaveLineNo = lineno != PNone.NONE && PyLongCheckExactNode.executeUncached(lineno); + final TruffleString msgStr = PyObjectStrAsTruffleStringNode.executeUncached(msg); if (filename == null && !heaveLineNo) { return msgStr; @@ -346,35 +418,33 @@ TruffleString str(VirtualFrame frame, PBaseException self, if (filename != null && heaveLineNo) { long ln; try { - ln = pyLongAsLongAndOverflowNode.execute(frame, inliningTarget, lineno); + ln = PyLongAsLongAndOverflowNode.executeUncached(lineno); } catch (OverflowException e) { ln = -1; } - result = simpleTruffleStringFormatNode.format("%s (%s, line %d)", msgStr, filename, ln); + result = SimpleTruffleStringFormatNode.getUncached().format("%s (%s, line %d)", msgStr, toTruffleStringUncached(filename), ln); } else if (filename != null) { - result = simpleTruffleStringFormatNode.format("%s (%s)", msgStr, filename); + result = SimpleTruffleStringFormatNode.getUncached().format("%s (%s)", msgStr, toTruffleStringUncached(filename)); } else { // only have_lineno long ln; try { - ln = pyLongAsLongAndOverflowNode.execute(frame, inliningTarget, lineno); + ln = PyLongAsLongAndOverflowNode.executeUncached(lineno); } catch (OverflowException e) { ln = -1; } - result = simpleTruffleStringFormatNode.format("%s (line %d)", msgStr, ln); + result = SimpleTruffleStringFormatNode.getUncached().format("%s (line %d)", msgStr, ln); } return result; } - TruffleString getLastPathElement(TruffleString path, TruffleString.FromJavaStringNode fromJavaStringNode, TruffleString.CodePointLengthNode codePointLengthNode, - TruffleString.LastIndexOfStringNode lastIndexOfStringNode, TruffleString.SubstringNode substringNode) { - int len = codePointLengthNode.execute(path, TS_ENCODING); - TruffleString sep = fromJavaStringNode.execute(getContext().getEnv().getFileNameSeparator(), TS_ENCODING); - int sepIdx = lastIndexOfStringNode.execute(path, sep, len, 0, TS_ENCODING); + String getLastPathElement(String path) { + int sepIdx = path.lastIndexOf(getContext().getEnv().getFileNameSeparator()); + sepIdx = Math.max(sepIdx, path.lastIndexOf('/')); if (sepIdx < 0) { return path; } - return substringNode.execute(path, sepIdx + 1, len - sepIdx - 1, TS_ENCODING, true); + return path.substring(sepIdx + 1); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeDecodeErrorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeDecodeErrorBuiltins.java index 7c591c77bc..6228089d17 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeDecodeErrorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeDecodeErrorBuiltins.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,7 +47,7 @@ import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.IDX_START; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.UNICODE_ERROR_ATTR_FACTORY; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsBytes; -import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsInt; +import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsLong; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsString; import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; @@ -78,7 +78,7 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; -import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; +import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.dsl.Bind; @@ -114,7 +114,7 @@ static Object initNoArgs(VirtualFrame frame, PBaseException self, Object[] args, @Bind Node inliningTarget, @Cached UnicodeErrorBuiltins.GetArgAsBytesNode getArgAsBytesNode, @Cached CastToTruffleStringNode toStringNode, - @Cached CastToJavaIntExactNode toJavaIntExactNode, + @Cached CastToJavaLongExactNode toJavaLongExactNode, @Cached BaseExceptionBuiltins.BaseExceptionInitNode baseInitNode, @Cached PRaiseNode raiseNode) { baseInitNode.execute(frame, self, args, keywords); @@ -122,8 +122,8 @@ static Object initNoArgs(VirtualFrame frame, PBaseException self, Object[] args, self.setExceptionAttributes(new Object[]{ getArgAsString(inliningTarget, args, 0, raiseNode, toStringNode), getArgAsBytes(frame, inliningTarget, args, 1, raiseNode, getArgAsBytesNode), - getArgAsInt(inliningTarget, args, 2, raiseNode, toJavaIntExactNode), - getArgAsInt(inliningTarget, args, 3, raiseNode, toJavaIntExactNode), + getArgAsLong(inliningTarget, args, 2, raiseNode, toJavaLongExactNode), + getArgAsLong(inliningTarget, args, 3, raiseNode, toJavaLongExactNode), getArgAsString(inliningTarget, args, 4, raiseNode, toStringNode) }); return PNone.NONE; @@ -148,12 +148,12 @@ TruffleString str(VirtualFrame frame, PBaseException self, // Get reason and encoding as strings, which they might not be if they've been // modified after we were constructed. PBytesLike object = (PBytesLike) attrNode.get(self, IDX_OBJECT, UNICODE_ERROR_ATTR_FACTORY); - final int start = attrNode.getInt(self, IDX_START, UNICODE_ERROR_ATTR_FACTORY); - final int end = attrNode.getInt(self, IDX_END, UNICODE_ERROR_ATTR_FACTORY); + final long start = attrNode.getLong(self, IDX_START, UNICODE_ERROR_ATTR_FACTORY); + final long end = attrNode.getLong(self, IDX_END, UNICODE_ERROR_ATTR_FACTORY); final TruffleString encoding = strNode.execute(frame, inliningTarget, attrNode.get(self, IDX_ENCODING, UNICODE_ERROR_ATTR_FACTORY)); final TruffleString reason = strNode.execute(frame, inliningTarget, attrNode.get(self, IDX_REASON, UNICODE_ERROR_ATTR_FACTORY)); if (start < object.getSequenceStorage().length() && end == start + 1) { - final int b = getitemNode.executeKnownInt(object.getSequenceStorage(), start); + final int b = getitemNode.executeKnownInt(object.getSequenceStorage(), (int) start); String bStr = PythonUtils.formatJString("%02x", b); return simpleTruffleStringFormatNode.format("'%s' codec can't decode byte 0x%s in position %d: %s", encoding, bStr, start, reason); } else { @@ -252,14 +252,14 @@ static int doIt(VirtualFrame frame, Node inliningTarget, PBaseException exceptio @Cached PyObjectSizeNode sizeNode) { Object obj = getObjectNode.execute(inliningTarget, exceptionObject); int size = sizeNode.execute(frame, inliningTarget, obj); - int start = attrNode.getInt(exceptionObject, IDX_START, UNICODE_ERROR_ATTR_FACTORY); + long start = attrNode.getLong(exceptionObject, IDX_START, UNICODE_ERROR_ATTR_FACTORY); if (start < 0) { start = 0; } if (start >= size) { start = size - 1; } - return start; + return (int) start; } } @@ -281,14 +281,14 @@ static int doIt(VirtualFrame frame, Node inliningTarget, PBaseException exceptio @Cached PyObjectSizeNode sizeNode) { Object obj = getObjectNode.execute(inliningTarget, exceptionObject); int size = sizeNode.execute(frame, inliningTarget, obj); - int end = attrNode.getInt(exceptionObject, IDX_END, UNICODE_ERROR_ATTR_FACTORY); + long end = attrNode.getLong(exceptionObject, IDX_END, UNICODE_ERROR_ATTR_FACTORY); if (end < 1) { end = 1; } if (end > size) { end = size; } - return end; + return (int) end; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeEncodeErrorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeEncodeErrorBuiltins.java index 42456539ce..8605180aa7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeEncodeErrorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeEncodeErrorBuiltins.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,7 +47,7 @@ import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.IDX_REASON; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.IDX_START; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.UNICODE_ERROR_ATTR_FACTORY; -import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsInt; +import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsLong; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsString; import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; @@ -72,7 +72,7 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; -import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; +import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.dsl.Bind; @@ -106,7 +106,7 @@ public abstract static class UnicodeEncodeErrorInitNode extends PythonBuiltinNod static Object initNoArgs(VirtualFrame frame, PBaseException self, Object[] args, PKeyword[] keywords, @Bind Node inliningTarget, @Cached CastToTruffleStringNode toStringNode, - @Cached CastToJavaIntExactNode toJavaIntExactNode, + @Cached CastToJavaLongExactNode toJavaLongExactNode, @Cached BaseExceptionBuiltins.BaseExceptionInitNode baseInitNode, @Cached PRaiseNode raiseNode) { baseInitNode.execute(frame, self, args, keywords); @@ -114,8 +114,8 @@ static Object initNoArgs(VirtualFrame frame, PBaseException self, Object[] args, self.setExceptionAttributes(new Object[]{ getArgAsString(inliningTarget, args, 0, raiseNode, toStringNode), getArgAsString(inliningTarget, args, 1, raiseNode, toStringNode), - getArgAsInt(inliningTarget, args, 2, raiseNode, toJavaIntExactNode), - getArgAsInt(inliningTarget, args, 3, raiseNode, toJavaIntExactNode), + getArgAsLong(inliningTarget, args, 2, raiseNode, toJavaLongExactNode), + getArgAsLong(inliningTarget, args, 3, raiseNode, toJavaLongExactNode), getArgAsString(inliningTarget, args, 4, raiseNode, toStringNode) }); return PNone.NONE; @@ -142,12 +142,12 @@ TruffleString str(VirtualFrame frame, PBaseException self, // Get reason and encoding as strings, which they might not be if they've been // modified after we were constructed. final TruffleString object = toTruffleStringNode.execute(inliningTarget, attrNode.get(self, IDX_OBJECT, UNICODE_ERROR_ATTR_FACTORY)); - final int start = attrNode.getInt(self, IDX_START, UNICODE_ERROR_ATTR_FACTORY); - final int end = attrNode.getInt(self, IDX_END, UNICODE_ERROR_ATTR_FACTORY); + final long start = attrNode.getLong(self, IDX_START, UNICODE_ERROR_ATTR_FACTORY); + final long end = attrNode.getLong(self, IDX_END, UNICODE_ERROR_ATTR_FACTORY); final TruffleString encoding = strNode.execute(frame, inliningTarget, attrNode.get(self, IDX_ENCODING, UNICODE_ERROR_ATTR_FACTORY)); final TruffleString reason = strNode.execute(frame, inliningTarget, attrNode.get(self, IDX_REASON, UNICODE_ERROR_ATTR_FACTORY)); if (start < codePointLengthNode.execute(object, TS_ENCODING) && end == start + 1) { - final int badChar = codePointAtIndexNode.execute(object, start); + final int badChar = codePointAtIndexNode.execute(object, (int) start); String badCharStr; if (badChar <= 0xFF) { badCharStr = PythonUtils.formatJString("\\x%02x", badChar); @@ -247,14 +247,14 @@ static int doIt(Node inliningTarget, PBaseException exceptionObject, @Cached TruffleString.CodePointLengthNode codePointLengthNode) { TruffleString ts = getObjectNode.execute(inliningTarget, exceptionObject); int size = codePointLengthNode.execute(ts, TS_ENCODING); - int start = attrNode.getInt(exceptionObject, IDX_START, UNICODE_ERROR_ATTR_FACTORY); + long start = attrNode.getLong(exceptionObject, IDX_START, UNICODE_ERROR_ATTR_FACTORY); if (start < 0) { start = 0; } if (start >= size) { start = size - 1; } - return start; + return (int) start; } } @@ -275,14 +275,14 @@ static int doIt(Node inliningTarget, PBaseException exceptionObject, @Cached TruffleString.CodePointLengthNode codePointLengthNode) { TruffleString ts = getObjectNode.execute(inliningTarget, exceptionObject); int size = codePointLengthNode.execute(ts, TS_ENCODING); - int end = attrNode.getInt(exceptionObject, IDX_END, UNICODE_ERROR_ATTR_FACTORY); + long end = attrNode.getLong(exceptionObject, IDX_END, UNICODE_ERROR_ATTR_FACTORY); if (end < 1) { end = 1; } if (end > size) { end = size; } - return end; + return (int) end; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeErrorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeErrorBuiltins.java index 5c5c16b309..9948488e2a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeErrorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeErrorBuiltins.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 @@ -60,6 +60,7 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; +import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData; import com.oracle.graal.python.runtime.object.PFactory; @@ -109,6 +110,14 @@ public static int getArgAsInt(Node inliningTarget, Object[] args, int index, PRa } } + public static long getArgAsLong(Node inliningTarget, Object[] args, int index, PRaiseNode raiseNode, CastToJavaLongExactNode castNode) { + if (args.length < index + 1 || !(PGuards.isInteger(args[index]) || PGuards.isPInt(args[index]))) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError); + } else { + return castNode.execute(inliningTarget, args[index]); + } + } + @GenerateInline @GenerateCached(false) public abstract static class GetArgAsBytesNode extends PNodeWithContext { @@ -194,17 +203,17 @@ static Object setInt(PBaseException self, int value, } @Specialization - static Object setInt(PBaseException self, long value, + static Object setLong(PBaseException self, long value, @Shared @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, (int) value, IDX_START, UNICODE_ERROR_ATTR_FACTORY); + return attrNode.execute(self, value, IDX_START, UNICODE_ERROR_ATTR_FACTORY); } @Specialization static Object setPInt(PBaseException self, PInt value, @Bind Node inliningTarget, - @Cached CastToJavaIntExactNode castToJavaIntExactNode, + @Cached CastToJavaLongExactNode castToJavaLongExactNode, @Shared @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, castToJavaIntExactNode.execute(inliningTarget, value), IDX_START, UNICODE_ERROR_ATTR_FACTORY); + return attrNode.execute(self, castToJavaLongExactNode.execute(inliningTarget, value), IDX_START, UNICODE_ERROR_ATTR_FACTORY); } @Specialization(guards = {"!isNoValue(value)", "!canBeInteger(value)"}) @@ -236,12 +245,18 @@ static Object setInt(PBaseException self, int value, return attrNode.execute(self, value, IDX_END, UNICODE_ERROR_ATTR_FACTORY); } + @Specialization + static Object setLong(PBaseException self, long value, + @Shared @Cached BaseExceptionAttrNode attrNode) { + return attrNode.execute(self, value, IDX_END, UNICODE_ERROR_ATTR_FACTORY); + } + @Specialization static Object setPInt(PBaseException self, PInt value, @Bind Node inliningTarget, - @Cached CastToJavaIntExactNode castToJavaIntExactNode, + @Cached CastToJavaLongExactNode castToJavaLongExactNode, @Shared @Cached BaseExceptionAttrNode attrNode) { - return attrNode.execute(self, castToJavaIntExactNode.execute(inliningTarget, value), IDX_END, UNICODE_ERROR_ATTR_FACTORY); + return attrNode.execute(self, castToJavaLongExactNode.execute(inliningTarget, value), IDX_END, UNICODE_ERROR_ATTR_FACTORY); } @Specialization(guards = {"!isNoValue(value)", "!canBeInteger(value)"}) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeTranslateErrorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeTranslateErrorBuiltins.java index e487569429..c60184d9bc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeTranslateErrorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeTranslateErrorBuiltins.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,7 +45,7 @@ import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.IDX_REASON; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.IDX_START; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.UNICODE_ERROR_ATTR_FACTORY; -import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsInt; +import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsLong; import static com.oracle.graal.python.builtins.objects.exception.UnicodeErrorBuiltins.getArgAsString; import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; @@ -67,7 +67,7 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; -import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; +import com.oracle.graal.python.nodes.util.CastToJavaLongExactNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.dsl.Bind; @@ -98,7 +98,7 @@ public abstract static class UnicodeTranslateErrorInitNode extends PythonBuiltin static Object initNoArgs(VirtualFrame frame, PBaseException self, Object[] args, PKeyword[] keywords, @Bind Node inliningTarget, @Cached CastToTruffleStringNode toStringNode, - @Cached CastToJavaIntExactNode toJavaIntExactNode, + @Cached CastToJavaLongExactNode toJavaLongExactNode, @Cached BaseExceptionBuiltins.BaseExceptionInitNode baseInitNode, @Cached PRaiseNode raiseNode) { baseInitNode.execute(frame, self, args, keywords); @@ -108,8 +108,8 @@ static Object initNoArgs(VirtualFrame frame, PBaseException self, Object[] args, // for the other attributes, although this exception does not have // an encoding set getArgAsString(inliningTarget, args, 0, raiseNode, toStringNode), - getArgAsInt(inliningTarget, args, 1, raiseNode, toJavaIntExactNode), - getArgAsInt(inliningTarget, args, 2, raiseNode, toJavaIntExactNode), + getArgAsLong(inliningTarget, args, 1, raiseNode, toJavaLongExactNode), + getArgAsLong(inliningTarget, args, 2, raiseNode, toJavaLongExactNode), getArgAsString(inliningTarget, args, 3, raiseNode, toStringNode) }); return PNone.NONE; @@ -136,11 +136,11 @@ TruffleString str(VirtualFrame frame, PBaseException self, // Get reason and encoding as strings, which they might not be if they've been // modified after we were constructed. final TruffleString object = toStringNode.execute(inliningTarget, attrNode.get(self, IDX_OBJECT, UNICODE_ERROR_ATTR_FACTORY)); - final int start = attrNode.getInt(self, IDX_START, UNICODE_ERROR_ATTR_FACTORY); - final int end = attrNode.getInt(self, IDX_END, UNICODE_ERROR_ATTR_FACTORY); + final long start = attrNode.getLong(self, IDX_START, UNICODE_ERROR_ATTR_FACTORY); + final long end = attrNode.getLong(self, IDX_END, UNICODE_ERROR_ATTR_FACTORY); final TruffleString reason = strNode.execute(frame, inliningTarget, attrNode.get(self, IDX_REASON, UNICODE_ERROR_ATTR_FACTORY)); if (start < codePointLengthNode.execute(object, TS_ENCODING) && end == start + 1) { - final int badChar = codePointAtIndexNode.execute(object, start); + final int badChar = codePointAtIndexNode.execute(object, (int) start); String badCharStr; if (badChar <= 0xFF) { badCharStr = PythonUtils.formatJString("\\x%02x", badChar); 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 0eb8a79a2b..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 @@ -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. @@ -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) { @@ -504,7 +504,15 @@ private static double doOperation(Node inliningTarget, double left, double right if (doSpecialCases(inliningTarget, left, right, raiseNode) == 1) { return 1.0; } - return Math.pow(left, right); + return doPow(inliningTarget, left, right, raiseNode); + } + + private static double doPow(Node inliningTarget, double left, double right, PRaiseNode raiseNode) { + double result = Math.pow(left, right); + if (Double.isInfinite(result) && Double.isFinite(left) && Double.isFinite(right)) { + throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.NUMERICAL_RESULT_OUT_OF_RANGE); + } + return result; } @Specialization(rewriteOn = UnexpectedResultException.class) @@ -522,7 +530,7 @@ static double doDD(VirtualFrame frame, double left, double right, @SuppressWarni PythonLanguage language = PythonLanguage.get(inliningTarget); throw new UnexpectedResultException(powerNode.execute(frame, PFactory.createComplex(language, left, 0), PFactory.createComplex(language, right, 0), none)); } - return Math.pow(left, right); + return doPow(inliningTarget, left, right, raiseNode); } @Specialization(replaces = "doDD") @@ -539,7 +547,7 @@ static Object doDDToComplex(VirtualFrame frame, double left, double right, PNone PythonLanguage language = PythonLanguage.get(inliningTarget); return powerNode.execute(frame, PFactory.createComplex(language, left, 0), PFactory.createComplex(language, right, 0), none); } - return Math.pow(left, right); + return doPow(inliningTarget, left, right, raiseNode); } @Specialization 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/foreign/ForeignAbstractClassBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignAbstractClassBuiltins.java index 1e57ae8fab..758766ca11 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignAbstractClassBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignAbstractClassBuiltins.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. @@ -76,7 +76,7 @@ */ @CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignAbstractClass) public final class ForeignAbstractClassBuiltins extends PythonBuiltins { - public static TpSlots SLOTS = ForeignAbstractClassBuiltinsSlotsGen.SLOTS; + public static final TpSlots SLOTS = ForeignAbstractClassBuiltinsSlotsGen.SLOTS; @Override protected List> getNodeFactories() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignBooleanBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignBooleanBuiltins.java index 2ffbd6eec7..968c6b6a1c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignBooleanBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignBooleanBuiltins.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. @@ -65,7 +65,7 @@ */ @CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignBoolean) public final class ForeignBooleanBuiltins extends PythonBuiltins { - public static TpSlots SLOTS = ForeignBooleanBuiltinsSlotsGen.SLOTS; + public static final TpSlots SLOTS = ForeignBooleanBuiltinsSlotsGen.SLOTS; @Override protected List> getNodeFactories() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignNumberBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignNumberBuiltins.java index 6312fcfdf8..98629ba20e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignNumberBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignNumberBuiltins.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. @@ -127,7 +127,7 @@ */ @CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignNumber) public final class ForeignNumberBuiltins extends PythonBuiltins { - public static TpSlots SLOTS = ForeignNumberBuiltinsSlotsGen.SLOTS; + public static final TpSlots SLOTS = ForeignNumberBuiltinsSlotsGen.SLOTS; @Override protected List> getNodeFactories() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java index f987533bd7..423c5d4b8e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignObjectBuiltins.java @@ -105,7 +105,7 @@ */ @CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignObject) public final class ForeignObjectBuiltins extends PythonBuiltins { - public static TpSlots SLOTS = ForeignObjectBuiltinsSlotsGen.SLOTS; + public static final TpSlots SLOTS = ForeignObjectBuiltinsSlotsGen.SLOTS; @Override protected List> getNodeFactories() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java new file mode 100644 index 0000000000..7ab72d1417 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/ForeignTimeZoneBuiltins.java @@ -0,0 +1,261 @@ +/* + * 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.builtins.objects.foreign; + +import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__; +import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Locale; + +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.builtins.CoreFunctions; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.PythonBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.DateTimeNodes; +import com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes; +import com.oracle.graal.python.builtins.modules.datetime.TimeDeltaNodes; +import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.DateTimeValue; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.type.TpSlots; +import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.lib.PyDateTimeCheckNode; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; +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.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Bind; +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.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; + +@CoreFunctions(extendClasses = PythonBuiltinClassType.ForeignTimeZone) +public final class ForeignTimeZoneBuiltins extends PythonBuiltins { + public static final TpSlots SLOTS = ForeignTimeZoneBuiltinsSlotsGen.SLOTS; + + @Override + protected List> getNodeFactories() { + return ForeignTimeZoneBuiltinsFactory.getFactories(); + } + + @Slot(value = SlotKind.tp_repr, isComplex = true) + @GenerateNodeFactory + abstract static class ReprNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static TruffleString repr(Object self, + @Bind Node inliningTarget) { + ZoneId zoneId = asZoneId(self); + TruffleString typeName = TypeNodes.GetTpNameNode.executeUncached(GetClassNode.executeUncached(self)); + String value = String.format("%s('%s')", typeName, zoneId.getId()); + return toTruffleStringUncached(value); + } + } + + @Slot(value = SlotKind.tp_str, isComplex = true) + @GenerateNodeFactory + abstract static class StrNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static TruffleString str(Object self) { + return toTruffleStringUncached(asZoneId(self).getId()); + } + } + + @Builtin(name = "utcoffset", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) + @GenerateNodeFactory + abstract static class UtcOffsetNode extends PythonBinaryBuiltinNode { + @Specialization + @TruffleBoundary + static Object utcoffset(Object self, Object dateTime, + @Bind Node inliningTarget) { + ZoneId zoneId = asZoneId(self); + if (dateTime == PNone.NONE) { + Object fixed = TemporalValueNodes.toFixedOffsetTimeZone(zoneId, inliningTarget); + if (fixed == null) { + return PNone.NONE; + } + return DatetimeModuleBuiltins.callUtcOffset(fixed, PNone.NONE, inliningTarget); + } + if (!PyDateTimeCheckNode.executeUncached(dateTime)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); + } + ZonedDateTime zonedDateTime = resolveDateTimeAtZone(TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime), zoneId); + return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, zonedDateTime.getOffset().getTotalSeconds(), 0, 0, 0, 0, 0); + } + } + + @Builtin(name = "dst", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) + @GenerateNodeFactory + abstract static class DstNode extends PythonBinaryBuiltinNode { + @Specialization + @TruffleBoundary + static Object dst(Object self, Object dateTime, + @Bind Node inliningTarget) { + ZoneId zoneId = asZoneId(self); + if (dateTime == PNone.NONE) { + return PNone.NONE; + } + if (!PyDateTimeCheckNode.executeUncached(dateTime)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); + } + ZonedDateTime zonedDateTime = resolveDateTimeAtZone(TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime), zoneId); + int dstSeconds = (int) zoneId.getRules().getDaylightSavings(zonedDateTime.toInstant()).getSeconds(); + return TimeDeltaNodes.NewNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PTimeDelta, 0, dstSeconds, 0, 0, 0, 0, 0); + } + } + + @Builtin(name = "tzname", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) + @GenerateNodeFactory + abstract static class TzNameNode extends PythonBinaryBuiltinNode { + @Specialization + @TruffleBoundary + static Object tzname(Object self, Object dateTime, + @Bind Node inliningTarget) { + ZoneId zoneId = asZoneId(self); + if (dateTime == PNone.NONE) { + return zoneId.getRules().isFixedOffset() ? toTruffleStringUncached(zoneId.getId()) : PNone.NONE; + } + if (!PyDateTimeCheckNode.executeUncached(dateTime)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); + } + String name = DateTimeFormatter.ofPattern("z", Locale.ENGLISH).format(resolveDateTimeAtZone(TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime), zoneId)); + return toTruffleStringUncached(name); + } + } + + @Builtin(name = "fromutc", minNumOfPositionalArgs = 1, parameterNames = {"$self", "dt"}) + @GenerateNodeFactory + abstract static class FromUtcNode extends PythonBinaryBuiltinNode { + @Specialization + @TruffleBoundary + static Object fromutc(Object self, Object dateTime, + @Bind Node inliningTarget) { + if (!PyDateTimeCheckNode.executeUncached(dateTime)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.FROMUTC_ARGUMENT_MUST_BE_A_DATETIME); + } + DateTimeValue asDateTime = TemporalValueNodes.GetDateTimeValue.executeUncached(inliningTarget, dateTime); + if (!hasMatchingTimeZone(asDateTime, self)) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.FROMUTC_DT_TZINFO_IS_NOT_SELF); + } + ZoneId zoneId = asZoneId(self); + LocalDateTime utcDateTime = asDateTime.toLocalDateTime(); + ZonedDateTime zonedDateTime = utcDateTime.atOffset(java.time.ZoneOffset.UTC).atZoneSameInstant(zoneId); + int fold = getFold(zonedDateTime); + return DateTimeNodes.NewUnsafeNode.getUncached().execute(inliningTarget, PythonBuiltinClassType.PDateTime, zonedDateTime.getYear(), zonedDateTime.getMonthValue(), + zonedDateTime.getDayOfMonth(), zonedDateTime.getHour(), zonedDateTime.getMinute(), zonedDateTime.getSecond(), zonedDateTime.getNano() / 1_000, self, fold); + } + } + + @Builtin(name = J___REDUCE__, minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class ReduceNode extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static Object reduce(Object self) { + return toTruffleStringUncached(asZoneId(self).getId()); + } + } + + private static ZoneId asZoneId(Object self) { + try { + return InteropLibrary.getUncached(self).asTimeZone(self); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + private static boolean hasMatchingTimeZone(DateTimeValue dateTime, Object self) { + if (dateTime.tzInfo == self) { + return true; + } + if (dateTime.zoneId != null) { + return asZoneId(self).equals(dateTime.zoneId); + } + if (dateTime.tzInfo == null) { + return false; + } + InteropLibrary interop = InteropLibrary.getUncached(dateTime.tzInfo); + if (!interop.isTimeZone(dateTime.tzInfo)) { + return false; + } + try { + return asZoneId(self).equals(interop.asTimeZone(dateTime.tzInfo)); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + + private static int getFold(ZonedDateTime zonedDateTime) { + List validOffsets = zonedDateTime.getZone().getRules().getValidOffsets(zonedDateTime.toLocalDateTime()); + if (validOffsets.size() < 2) { + return 0; + } + return zonedDateTime.getOffset().equals(validOffsets.get(validOffsets.size() - 1)) ? 1 : 0; + } + + @TruffleBoundary + private static ZonedDateTime resolveDateTimeAtZone(DateTimeValue dateTime, ZoneId zoneId) { + LocalDateTime localDateTime = dateTime.toLocalDateTime(); + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + List validOffsets = zoneId.getRules().getValidOffsets(localDateTime); + if (validOffsets.size() < 2) { + return zonedDateTime; + } + return dateTime.fold == 1 ? zonedDateTime.withLaterOffsetAtOverlap() : zonedDateTime.withEarlierOffsetAtOverlap(); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameBuiltins.java index 69d8e9b095..082fd96a02 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/FrameBuiltins.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. @@ -62,7 +62,6 @@ import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.OverflowException; import com.oracle.truffle.api.CompilerDirectives; -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.GenerateNodeFactory; @@ -253,11 +252,9 @@ public abstract static class GetCodeNode extends PythonBuiltinNode { public abstract PCode executeObject(VirtualFrame frame, PFrame self); @Specialization - static PCode get(PFrame self, - @Bind PythonLanguage language) { - RootCallTarget ct = self.getTarget(); - assert ct != null; - return PFactory.createCode(language, ct); + static Object get(PFrame self) { + PCode code = self.getCode(); + return code != null ? code : PNone.NONE; } @NeverDefault @@ -287,7 +284,8 @@ public abstract static class GetBackrefNode extends PythonBuiltinNode { @Specialization Object getBackref(VirtualFrame frame, PFrame self, @Cached ReadFrameNode readCallerFrame) { - PFrame backref = readCallerFrame.getFrameForReference(frame, self.getRef(), 1, 0); + PFrame backref = readCallerFrame.getFrameForReference(frame, self.getRef(), ReadFrameNode.AllPythonFramesSelector.INSTANCE, 1, 0, + self.getThread()); if (backref != null) { backref.getRef().markAsEscaped(); return backref; 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 dc9d895240..cf04a9fc5c 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 @@ -42,9 +42,9 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.objects.code.CodeNodes.GetCodeRootNode; import com.oracle.graal.python.builtins.objects.code.PCode; import com.oracle.graal.python.builtins.objects.function.PArguments; +import com.oracle.graal.python.builtins.objects.function.PFunction; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorRootNode; @@ -55,7 +55,6 @@ import com.oracle.graal.python.nodes.frame.ReadFrameNode; import com.oracle.graal.python.runtime.CallerFlags; 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.RootCallTarget; import com.oracle.truffle.api.bytecode.BytecodeFrame; @@ -91,7 +90,8 @@ public final class PFrame extends PythonBuiltinObject { * {@link BytecodeNode} that was executed at the time when the BCI was captured. */ private Node location; - private RootCallTarget callTarget; + private PFunction function; + private PCode code; private int line = UNINITIALIZED_LINE; private int bci = -1; @@ -111,8 +111,6 @@ public final class PFrame extends PythonBuiltinObject { private boolean traceLine = true; - private PFrame.Reference backref = null; - /** * The last {@link CallerFlags} that were used the last time the frame was synced or passed down * to a callee. See {@link #needsRefresh(VirtualFrame, int)} for more details. @@ -207,10 +205,15 @@ public Reference getCallerInfo() { } } - public PFrame(PythonLanguage lang, Reference virtualFrameInfo, Node location, boolean hasCustomLocals) { + public PFrame(PythonLanguage lang, Reference virtualFrameInfo, Node location, Object functionOrCode, boolean hasCustomLocals) { super(PythonBuiltinClassType.PFrame, PythonBuiltinClassType.PFrame.getInstanceShape(lang)); this.virtualFrameInfo = virtualFrameInfo; this.location = location; + if (functionOrCode instanceof PFunction function) { + this.function = function; + } else if (functionOrCode instanceof PCode code) { + this.code = code; + } this.hasCustomLocals = hasCustomLocals; // Mark everything as current for now. MaterializeFrameNode will set lastCallerFlags to a // narrower value if needed @@ -218,11 +221,12 @@ public PFrame(PythonLanguage lang, Reference virtualFrameInfo, Node location, bo 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; - this.location = GetCodeRootNode.executeUncached(code); + this.code = code; + this.location = code.getRootNode(); Reference curFrameInfo = new Reference(location != null ? location.getRootNode() : null, null); this.virtualFrameInfo = curFrameInfo; curFrameInfo.setPyFrame(this); @@ -383,14 +387,14 @@ public PythonObject getGlobals() { } public RootCallTarget getTarget() { - if (callTarget == null) { - if (location != null) { - callTarget = PythonUtils.getOrCreateCallTarget(location.getRootNode()); - } else if (getRef() != null && getRef().getRootNode() != null) { - callTarget = PythonUtils.getOrCreateCallTarget(getRef().getRootNode()); - } + return getCode().getRootCallTarget(); + } + + public PCode getCode() { + if (code == null && function != null) { + code = function.getCode(); } - return callTarget; + return code; } public void setGlobals(PythonObject globals) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java index 273a1b29b3..44e6836809 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/AbstractFunctionBuiltins.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. @@ -348,8 +348,12 @@ static Object setFunction(PFunction self, Object value, @Specialization(guards = "isNoValue(none)") @TruffleBoundary - static TruffleString getBuiltin(PBuiltinFunction self, @SuppressWarnings("unused") PNone none, + static Object getBuiltin(PBuiltinFunction self, @SuppressWarnings("unused") PNone none, @Bind Node inliningTarget) { + Object storedSignature = ReadAttributeFromObjectNode.getUncached().execute(self, T___TEXT_SIGNATURE__); + if (storedSignature != PNone.NO_VALUE) { + return storedSignature; + } Signature signature = self.getSignature(); if (signature.isHidden()) { throw PRaiseNode.raiseStatic(inliningTarget, AttributeError, ErrorMessages.HAS_NO_ATTR, self, T___TEXT_SIGNATURE__); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java index f6a62ca9f6..6b5af3e289 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.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. @@ -64,6 +64,8 @@ import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKey; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorNext; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorValue; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageLen; +import com.oracle.graal.python.builtins.objects.common.KeywordsStorage; import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorDeleteMarker; @@ -295,13 +297,13 @@ public abstract static class GetKeywordDefaultsNode extends PythonBinaryBuiltinN @Specialization(guards = "isNoValue(arg)") static Object get(PFunction self, @SuppressWarnings("unused") PNone arg, @Bind PythonLanguage language) { - PKeyword[] kwdefaults = self.getKwDefaults(); - return (kwdefaults.length > 0) ? PFactory.createDict(language, kwdefaults) : PNone.NONE; + return self.getKwDefaultsDict(language); } @Specialization(guards = "!isNoValue(arg)") - static Object set(PFunction self, @SuppressWarnings("unused") PNone arg) { - self.setKwDefaults(PKeyword.EMPTY_KEYWORDS); + static Object set(PFunction self, @SuppressWarnings("unused") PNone arg, + @Bind PythonLanguage language) { + self.setKwDefaultsDict(PFactory.createDict(language, PKeyword.EMPTY_KEYWORDS)); return PNone.NONE; } @@ -315,12 +317,16 @@ Object set(PFunction self, PDict arg) { Object key = assertNoJavaString(HashingStorageIteratorKey.executeUncached(storage, it)); if (key instanceof PString) { key = ((PString) key).getValueUncached(); - } else if (!(key instanceof TruffleString)) { - throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.TypeError, ErrorMessages.KEYWORD_NAMES_MUST_BE_STR_GOT_P, key); } - keywords.add(new PKeyword((TruffleString) key, HashingStorageIteratorValue.executeUncached(storage, it))); + if (key instanceof TruffleString) { + keywords.add(new PKeyword((TruffleString) key, HashingStorageIteratorValue.executeUncached(storage, it))); + } + } + PKeyword[] kwdefaults = keywords.isEmpty() ? PKeyword.EMPTY_KEYWORDS : keywords.toArray(new PKeyword[keywords.size()]); + if (kwdefaults.length == HashingStorageLen.executeUncached(storage)) { + arg.setDictStorage(new KeywordsStorage(kwdefaults)); } - self.setKwDefaults(keywords.isEmpty() ? PKeyword.EMPTY_KEYWORDS : keywords.toArray(new PKeyword[keywords.size()])); + self.setKwDefaultsDict(arg); return PNone.NONE; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/MethodDescriptorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/MethodDescriptorBuiltins.java index 106f225cbb..454e004cbd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/MethodDescriptorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/MethodDescriptorBuiltins.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 @@ -56,13 +56,16 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +import com.oracle.graal.python.nodes.object.DescriptorCheckNode; 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.GenerateNodeFactory; 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.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.PBuiltinFunction) @@ -92,13 +95,19 @@ static Object doFunction(PFunction self, Object instance, Object klass) { @Specialization(guards = {"!isNoValue(instance)", "!self.needsDeclaringType()"}) static PBuiltinMethod doBuiltinMethod(PBuiltinFunction self, Object instance, Object klass, - @Bind PythonLanguage language) { + @Bind PythonLanguage language, + @Bind Node inliningTarget, + @Shared("descriptorCheckNode") @Cached DescriptorCheckNode descriptorCheckNode) { + checkBuiltinDescriptorReceiver(self, instance, inliningTarget, descriptorCheckNode); return PFactory.createBuiltinMethod(language, instance, self); } @Specialization(guards = {"!isNoValue(instance)", "self.needsDeclaringType()"}) static PBuiltinMethod doBuiltinMethodWithDeclaringClass(PBuiltinFunction self, Object instance, Object klass, - @Bind PythonLanguage language) { + @Bind PythonLanguage language, + @Bind Node inliningTarget, + @Shared("descriptorCheckNode") @Cached DescriptorCheckNode descriptorCheckNode) { + checkBuiltinDescriptorReceiver(self, instance, inliningTarget, descriptorCheckNode); return PFactory.createBuiltinMethod(language, instance, self, self.getEnclosingType()); } @@ -106,6 +115,14 @@ static PBuiltinMethod doBuiltinMethodWithDeclaringClass(PBuiltinFunction self, O static Object doBuiltinFunction(PBuiltinFunction self, Object instance, Object klass) { return self; } + + private static void checkBuiltinDescriptorReceiver(PBuiltinFunction self, Object instance, Node inliningTarget, DescriptorCheckNode descriptorCheckNode) { + Object enclosingType = self.getEnclosingType(); + if (enclosingType == null) { + return; + } + descriptorCheckNode.execute(inliningTarget, enclosingType, self.getName(), instance); + } } @Slot(value = SlotKind.tp_repr, isComplex = true) @@ -113,14 +130,14 @@ static Object doBuiltinFunction(PBuiltinFunction self, Object instance, Object k abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization(guards = "self.getEnclosingType() == null") static TruffleString reprModuleFunction(PBuiltinFunction self, - @Cached.Shared("formatter") @Cached StringUtils.SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { + @Shared("formatter") @Cached StringUtils.SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { // (tfel): these really shouldn't be accessible, I think return simpleTruffleStringFormatNode.format("", self.getName()); } @Specialization(guards = "self.getEnclosingType() != null") static TruffleString reprClassFunction(PBuiltinFunction self, - @Cached.Shared("formatter") @Cached StringUtils.SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { + @Shared("formatter") @Cached StringUtils.SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { return simpleTruffleStringFormatNode.format("", self.getName(), TypeNodes.GetNameNode.executeUncached(self.getEnclosingType())); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java index 2790c91638..ae37c1a3ab 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java @@ -25,6 +25,7 @@ */ package com.oracle.graal.python.builtins.objects.function; +import com.oracle.graal.python.builtins.objects.code.PCode; import com.oracle.graal.python.builtins.objects.frame.PFrame; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.PythonObject; @@ -39,7 +40,7 @@ *
      *
    • {@code SPECIAL_ARGUMENT (Object)}
    • *
    • {@code INDEX_GLOBALS_ARGUMENT (PythonObject)}
    • - *
    • {@code INDEX_FUNCTION_OBJECT (PFunction)}
    • + *
    • {@code INDEX_FUNCTION_OR_CODE_OBJECT (PFunction)}
    • *
    • {@code INDEX_CALLER_FRAME_INFO (PFrame.Reference)}
    • *
    • {@code INDEX_CURRENT_FRAME_INFO (PFrame.Reference)}
    • *
    • {@code INDEX_CURRENT_EXCEPTION (PException)}
    • @@ -58,12 +59,20 @@ public final class PArguments { private static final int INDEX_SPECIAL_ARGUMENT = 0; private static final int INDEX_GLOBALS_ARGUMENT = 1; - private static final int INDEX_FUNCTION_OBJECT = 2; + private static final int INDEX_FUNCTION_OR_CODE_OBJECT = 2; private static final int INDEX_CALLER_FRAME_INFO = 3; private static final int INDEX_CURRENT_FRAME_INFO = 4; private static final int INDEX_CURRENT_EXCEPTION = 5; public static final int USER_ARGUMENTS_OFFSET = 6; + public static boolean assertIsPythonFrame(Frame frame) { + assert frame != null; + Object[] frameArgs = frame.getArguments(); + assert frameArgs.length >= USER_ARGUMENTS_OFFSET : frameArgs.length; + assert frameArgs[INDEX_CURRENT_FRAME_INFO] instanceof PFrame.Reference : frameArgs[INDEX_CURRENT_FRAME_INFO]; + return true; + } + public static boolean isPythonFrame(Frame frame) { return frame != null && isPythonFrame(frame.getArguments()); } @@ -72,10 +81,11 @@ public static boolean isPythonFrame(Object[] frameArgs) { return frameArgs.length >= USER_ARGUMENTS_OFFSET && frameArgs[INDEX_CURRENT_FRAME_INFO] instanceof PFrame.Reference; } - public static Object[] withGlobals(PythonModule globals) { + public static Object[] withGlobals(PCode code, PythonModule globals) { CompilerAsserts.neverPartOfCompilation(); Object[] arguments = create(); setGlobals(arguments, globals.getDict()); + setCodeObject(arguments, code); return arguments; } @@ -192,12 +202,42 @@ public static void setExceptionUnchecked(Object[] arguments, Object exc) { arguments[INDEX_CURRENT_EXCEPTION] = exc; } + public static Object getFunctionOrCodeObject(Object[] arguments) { + return arguments[INDEX_FUNCTION_OR_CODE_OBJECT]; + } + + public static Object getFunctionOrCodeObject(Frame frame) { + return getFunctionOrCodeObject(frame.getArguments()); + } + public static PFunction getFunctionObject(Object[] arguments) { - return (PFunction) arguments[INDEX_FUNCTION_OBJECT]; + return (PFunction) arguments[INDEX_FUNCTION_OR_CODE_OBJECT]; + } + + public static PFunction getFunctionObject(Frame frame) { + return getFunctionObject(frame.getArguments()); + } + + public static PCode getCodeObject(Object[] arguments) { + Object functionOrCodeObject = getFunctionOrCodeObject(arguments); + if (functionOrCodeObject instanceof PFunction function) { + return function.getCode(); + } else if (functionOrCodeObject instanceof PCode code) { + return code; + } + return null; + } + + public static PCode getCodeObject(Frame frame) { + return getCodeObject(frame.getArguments()); } public static void setFunctionObject(Object[] arguments, PFunction function) { - arguments[INDEX_FUNCTION_OBJECT] = function; + arguments[INDEX_FUNCTION_OR_CODE_OBJECT] = function; + } + + public static void setCodeObject(Object[] arguments, PCode code) { + arguments[INDEX_FUNCTION_OR_CODE_OBJECT] = code; } public static void setArgument(Object[] arguments, int index, Object value) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java index 972dee05cb..a41da7f09d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PBuiltinFunction.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. @@ -26,6 +26,7 @@ package com.oracle.graal.python.builtins.objects.function; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___TEXT_SIGNATURE__; import static com.oracle.graal.python.nodes.StringLiterals.T_DOT; import java.util.Arrays; @@ -43,12 +44,12 @@ import com.oracle.graal.python.builtins.objects.str.StringUtils; import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetNameNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlot; -import com.oracle.graal.python.nodes.PRootNode; import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; 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.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; @@ -71,11 +72,10 @@ */ @ExportLibrary(InteropLibrary.class) public final class PBuiltinFunction extends PythonBuiltinObject implements BoundBuiltinCallable { - private final PString name; private final TruffleString qualname; private final Object enclosingType; - private final RootCallTarget callTarget; + private final RootNode functionRootNode; private final Signature signature; private final int flags; private final TpSlot slot; @@ -83,7 +83,10 @@ public final class PBuiltinFunction extends PythonBuiltinObject implements Bound @CompilationFinal(dimensions = 1) private final Object[] defaults; @CompilationFinal(dimensions = 1) private final PKeyword[] kwDefaults; - public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, int flags, RootCallTarget callTarget, + @CompilationFinal private RootCallTarget callTarget; + + public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, int flags, RootNode functionRootNode, + Signature signature, TpSlot slot, PExternalFunctionWrapper slotWrapper) { super(cls, shape); this.name = PythonUtils.toPString(name); @@ -93,8 +96,8 @@ public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString n this.qualname = name; } this.enclosingType = enclosingType; - this.callTarget = callTarget; - this.signature = ((PRootNode) callTarget.getRootNode()).getSignature(); + this.functionRootNode = functionRootNode; + this.signature = signature; this.flags = flags; this.defaults = defaults; this.kwDefaults = kwDefaults != null ? kwDefaults : generateKwDefaults(signature); @@ -102,10 +105,6 @@ public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString n this.slotWrapper = slotWrapper; } - public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, int flags, RootCallTarget callTarget) { - this(cls, shape, name, enclosingType, defaults, kwDefaults, flags, callTarget, null, null); - } - public static PKeyword[] generateKwDefaults(Signature signature) { TruffleString[] keywordNames = signature.getKeywordNames(); PKeyword[] kwDefaults = PKeyword.create(keywordNames.length); @@ -129,7 +128,7 @@ public static Object[] generateDefaults(int numDefaults) { } public RootNode getFunctionRootNode() { - return callTarget.getRootNode(); + return functionRootNode; } /** @@ -149,12 +148,7 @@ public PExternalFunctionWrapper getSlotWrapper() { } public NodeFactory getBuiltinNodeFactory() { - RootNode functionRootNode = getFunctionRootNode(); - if (functionRootNode instanceof BuiltinFunctionRootNode builtinRoot) { - return builtinRoot.getFactory(); - } else { - return null; - } + return functionRootNode instanceof BuiltinFunctionRootNode builtinRoot ? builtinRoot.getFactory() : null; } public int getFlags() { @@ -169,11 +163,6 @@ public boolean needsDeclaringType() { return (flags & CExtContext.METH_METHOD) != 0; } - @TruffleBoundary - public static int getFlags(Builtin builtin, RootCallTarget callTarget) { - return getFlags(builtin, ((PRootNode) callTarget.getRootNode()).getSignature()); - } - @TruffleBoundary public static int getFlags(Builtin builtin, Signature signature) { if (builtin == null) { @@ -211,7 +200,32 @@ public Signature getSignature() { } public RootCallTarget getCallTarget() { - return callTarget; + RootCallTarget ct = callTarget; + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, ct == null)) { + if (CompilerDirectives.inCompiledCode() && CompilerDirectives.isPartialEvaluationConstant(this)) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + } + ct = initializeCallTarget(); + } + return ct; + } + + @TruffleBoundary + private RootCallTarget initializeCallTarget() { + RootCallTarget ct = callTarget; + if (ct == null) { + ct = functionRootNode.getCallTarget(); + callTarget = ct; + } + return ct; + } + + public boolean declaresExplicitSelf() { + return !(functionRootNode instanceof BuiltinFunctionRootNode builtinRoot) || builtinRoot.declaresExplicitSelf(); + } + + public boolean forceSplitDirectCalls() { + return functionRootNode instanceof BuiltinFunctionRootNode builtinRoot && builtinRoot.getBuiltin().forceSplitDirectCalls(); } public TruffleString getName() { @@ -243,6 +257,10 @@ public PBuiltinFunction boundToObject(PythonBuiltinClassType klass, PythonLangua } else { PBuiltinFunction func = PFactory.createBuiltinFunction(language, this, klass); func.setAttribute(T___DOC__, getAttribute(T___DOC__)); + Object textSignature = getAttribute(T___TEXT_SIGNATURE__); + if (textSignature != PNone.NO_VALUE) { + func.setAttribute(T___TEXT_SIGNATURE__, textSignature); + } return func; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java index 5bd0acfece..b95a1bbc37 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PFunction.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. @@ -25,18 +25,28 @@ */ package com.oracle.graal.python.builtins.objects.function; +import java.util.ArrayList; + 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.cell.PCell; -import com.oracle.graal.python.builtins.objects.code.CodeNodes.GetCodeCallTargetNode; import com.oracle.graal.python.builtins.objects.code.PCode; +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; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKey; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorNext; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorValue; +import com.oracle.graal.python.builtins.objects.common.KeywordsStorage; +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.str.PString; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.PRootNode; -import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetCallTargetNode; import com.oracle.graal.python.runtime.GilNode; +import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; @@ -45,14 +55,12 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; -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.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.nodes.RootNode; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.strings.TruffleString; @@ -72,6 +80,7 @@ public final class PFunction extends PythonObject { private Object[] defaultValues; @CompilationFinal(dimensions = 1) private PKeyword[] finalKwDefaultValues; private PKeyword[] kwDefaultValues; + private PDict kwDefaultDict; private Object doc; public PFunction(PythonLanguage lang, TruffleString name, TruffleString qualname, PCode code, PythonObject globals, PCell[] closure) { @@ -90,7 +99,6 @@ public PFunction(PythonLanguage lang, TruffleString name, TruffleString qualname this.qualname = qualname; assert code != null; this.code = this.finalCode = code; - this.callTarget = code.getRootCallTarget(); this.globals = globals; this.defaultValues = this.finalDefaultValues = defaultValues == null ? PythonUtils.EMPTY_OBJECT_ARRAY : defaultValues; this.kwDefaultValues = this.finalKwDefaultValues = kwDefaultValues == null ? PKeyword.EMPTY_KEYWORDS : kwDefaultValues; @@ -171,7 +179,12 @@ public PCode getCode() { } public RootCallTarget getCallTarget() { - return callTarget; + RootCallTarget ct = callTarget; + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, ct == null)) { + ct = getCode().getRootCallTarget(); + callTarget = ct; + } + return ct; } @TruffleBoundary @@ -180,7 +193,7 @@ public void setCode(PCode code) { assert code != null : "code cannot be null"; this.finalCode = null; this.code = code; - this.callTarget = code.getRootCallTarget(); + this.callTarget = null; } public Object[] getDefaults() { @@ -202,22 +215,58 @@ public void setDefaults(Object[] defaults) { public PKeyword[] getKwDefaults() { if (CompilerDirectives.inCompiledCode() && CompilerDirectives.isPartialEvaluationConstant(this)) { if (codeStableAssumption.isValid()) { + assert kwDefaultDict == null; return finalKwDefaultValues; } } + if (kwDefaultDict != null) { + HashingStorage storage = kwDefaultDict.getDictStorage(); + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.LIKELY_PROBABILITY, storage instanceof KeywordsStorage)) { + return ((KeywordsStorage) storage).getStore(); + } + return kwDefaultsFromStorage(storage); + } return kwDefaultValues; } + public Object getKwDefaultsDict(PythonLanguage language) { + if (kwDefaultDict == null) { + PKeyword[] kwdefaults = getKwDefaults(); + if (kwdefaults.length == 0) { + return PNone.NONE; + } + setKwDefaultsDict(PFactory.createDict(language, kwdefaults)); + } + return kwDefaultDict; + } + @TruffleBoundary - public void setKwDefaults(PKeyword[] defaults) { + public void setKwDefaultsDict(PDict defaults) { this.codeStableAssumption.invalidate("kw defaults changed for function " + getName()); - this.finalDefaultValues = null; // avoid leak, and make code that wrongly uses it crash - this.kwDefaultValues = defaults; + this.finalKwDefaultValues = null; // avoid leak, and make code that wrongly uses it crash + this.kwDefaultValues = PKeyword.EMPTY_KEYWORDS; + this.kwDefaultDict = defaults; + } + + @TruffleBoundary + private static PKeyword[] kwDefaultsFromStorage(HashingStorage storage) { + ArrayList keywords = new ArrayList<>(); + HashingStorageIterator it = HashingStorageGetIterator.executeUncached(storage); + while (HashingStorageIteratorNext.executeUncached(storage, it)) { + Object key = HashingStorageIteratorKey.executeUncached(storage, it); + if (key instanceof PString) { + key = ((PString) key).getValueUncached(); + } + if (key instanceof TruffleString) { + keywords.add(new PKeyword((TruffleString) key, HashingStorageIteratorValue.executeUncached(storage, it))); + } + } + return keywords.isEmpty() ? PKeyword.EMPTY_KEYWORDS : keywords.toArray(new PKeyword[keywords.size()]); } @TruffleBoundary String getSourceCode() { - RootNode rootNode = GetCallTargetNode.getUncached().execute(this).getRootNode(); + RootNode rootNode = getCode().getRootNode(); SourceSection sourceSection = rootNode.getSourceSection(); if (sourceSection != null) { return sourceSection.getCharacters().toString(); @@ -244,12 +293,10 @@ String getExecutableName(@Shared("gil") @Cached GilNode gil, @ExportMessage public SourceSection getSourceLocation( - @Bind Node inliningTarget, - @Shared("getCt") @Cached GetCodeCallTargetNode getCt, @Shared("gil") @Cached GilNode gil) throws UnsupportedMessageException { boolean mustRelease = gil.acquire(); try { - SourceSection result = getSourceLocationDirect(inliningTarget, getCt); + SourceSection result = getSourceLocationDirect(); if (result == null) { throw UnsupportedMessageException.create(); } else { @@ -261,8 +308,8 @@ public SourceSection getSourceLocation( } @TruffleBoundary - private SourceSection getSourceLocationDirect(Node inliningTarget, GetCodeCallTargetNode getCt) { - RootNode rootNode = getCt.execute(inliningTarget, code).getRootNode(); + private SourceSection getSourceLocationDirect() { + RootNode rootNode = code.getRootNode(); SourceSection result; if (rootNode instanceof PRootNode) { result = ((PRootNode) rootNode).getSourceSection(); @@ -279,12 +326,10 @@ private static SourceSection getForeignSourceSection(RootNode rootNode) { @ExportMessage public boolean hasSourceLocation( - @Bind Node inliningTarget, - @Shared("getCt") @Cached GetCodeCallTargetNode getCt, @Shared("gil") @Cached GilNode gil) { boolean mustRelease = gil.acquire(); try { - return getSourceLocationDirect(inliningTarget, getCt) != null; + return getSourceLocationDirect() != null; } finally { gil.release(mustRelease); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/WrapperDescriptorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/WrapperDescriptorBuiltins.java index 78e76ee624..938912ba2b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/WrapperDescriptorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/WrapperDescriptorBuiltins.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 @@ -56,6 +56,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +import com.oracle.graal.python.nodes.object.DescriptorCheckNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -63,6 +64,7 @@ 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.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.WrapperDescriptor) @@ -92,7 +94,10 @@ static Object doFunction(PFunction self, Object instance, Object klass) { @Specialization(guards = {"!isNoValue(instance)"}) static PBuiltinMethod doBuiltinMethod(PBuiltinFunction self, Object instance, Object klass, - @Bind PythonLanguage language) { + @Bind PythonLanguage language, + @Bind Node inliningTarget, + @Cached DescriptorCheckNode descriptorCheckNode) { + checkBuiltinDescriptorReceiver(self, instance, inliningTarget, descriptorCheckNode); return PFactory.createBuiltinMethod(PythonBuiltinClassType.MethodWrapper, PythonBuiltinClassType.MethodWrapper.getInstanceShape(language), instance, self); } @@ -100,6 +105,14 @@ static PBuiltinMethod doBuiltinMethod(PBuiltinFunction self, Object instance, Ob static Object doBuiltinFunction(PBuiltinFunction self, Object instance, Object klass) { return self; } + + private static void checkBuiltinDescriptorReceiver(PBuiltinFunction self, Object instance, Node inliningTarget, DescriptorCheckNode descriptorCheckNode) { + Object enclosingType = self.getEnclosingType(); + if (enclosingType == null) { + return; + } + descriptorCheckNode.execute(inliningTarget, enclosingType, self.getName(), instance); + } } @Slot(value = SlotKind.tp_repr, isComplex = true) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CommonGeneratorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CommonGeneratorBuiltins.java index b227c0a6e9..59400c4693 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CommonGeneratorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CommonGeneratorBuiltins.java @@ -72,6 +72,7 @@ import com.oracle.graal.python.nodes.bytecode.FrameInfo; import com.oracle.graal.python.nodes.bytecode.GeneratorReturnException; import com.oracle.graal.python.nodes.bytecode.GeneratorYieldResult; +import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.call.CallDispatchers; import com.oracle.graal.python.nodes.frame.MaterializeFrameNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -398,13 +399,13 @@ static Object sendThrow(VirtualFrame frame, PGenerator self, Object typ, Object Node location; RootNode rootNode = self.getCurrentCallTarget().getRootNode(); if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { - location = self.getBytecodeNode(); + location = ((PBytecodeDSLRootNode) self.getRootNode()).getBytecodeNode(); } else { location = rootNode; } MaterializedFrame generatorFrame = self.getGeneratorFrame(); PFrame.Reference ref = new PFrame.Reference(rootNode, PFrame.Reference.EMPTY); - PFrame pFrame = MaterializeFrameNode.materializeGeneratorFrame(PythonLanguage.get(inliningTarget), location, generatorFrame, self.getGlobals(), ref); + PFrame pFrame = MaterializeFrameNode.materializeGeneratorFrame(PythonLanguage.get(inliningTarget), location, generatorFrame, self.getGeneratorFunction(), self.getGlobals(), ref); FrameInfo info = (FrameInfo) generatorFrame.getFrameDescriptor().getInfo(); pFrame.setLine(info.getFirstLineNumber()); Object existingTracebackObj = getTracebackNode.execute(inliningTarget, instance); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CoroutineBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CoroutineBuiltins.java index 98a8cd8bc5..e6ba012b69 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CoroutineBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/CoroutineBuiltins.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 @@ -62,8 +62,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.nodes.Node; -import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.PCoroutine) @@ -80,10 +78,8 @@ protected List> getNodeFa @GenerateNodeFactory public abstract static class GetCode extends PythonUnaryBuiltinNode { @Specialization - static Object getCode(PGenerator self, - @Bind Node inliningTarget, - @Cached InlinedConditionProfile hasCodeProfile) { - return self.getOrCreateCode(inliningTarget, hasCodeProfile); + static Object getCode(PGenerator self) { + return self.getGeneratorFunction().getCode(); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java index b48d1fee65..807750f148 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java @@ -71,7 +71,6 @@ import com.oracle.truffle.api.frame.MaterializedFrame; 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; @CoreFunctions(extendClasses = PythonBuiltinClassType.PGenerator) @@ -160,10 +159,8 @@ static Object next(VirtualFrame frame, PGenerator self, @GenerateNodeFactory public abstract static class GetCodeNode extends PythonUnaryBuiltinNode { @Specialization - static Object getCode(PGenerator self, - @Bind Node inliningTarget, - @Cached InlinedConditionProfile hasCodeProfile) { - return self.getOrCreateCode(inliningTarget, hasCodeProfile); + static Object getCode(PGenerator self) { + return self.getGeneratorFunction().getCode(); } } @@ -207,7 +204,8 @@ static Object getFrame(VirtualFrame frame, PGenerator self, if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { BytecodeDSLFrameInfo info = (BytecodeDSLFrameInfo) generatorFrame.getFrameDescriptor().getInfo(); if (pyFrame == null) { - pyFrame = MaterializeFrameNode.materializeGeneratorFrame(PythonLanguage.get(readFrameNode), self.getBytecodeNode(), generatorFrame, self.getGlobals(), currentRef); + pyFrame = MaterializeFrameNode.materializeGeneratorFrame(PythonLanguage.get(readFrameNode), self.getBytecodeNode(), generatorFrame, self.getGeneratorFunction(), + self.getGlobals(), currentRef); } BytecodeLocation location = self.getCurrentLocation(); if (location != null) { @@ -221,7 +219,8 @@ static Object getFrame(VirtualFrame frame, PGenerator self, } else { BytecodeFrameInfo info = (BytecodeFrameInfo) generatorFrame.getFrameDescriptor().getInfo(); if (pyFrame == null) { - pyFrame = MaterializeFrameNode.materializeGeneratorFrame(PythonLanguage.get(readFrameNode), info.getRootNode(), generatorFrame, self.getGlobals(), currentRef); + pyFrame = MaterializeFrameNode.materializeGeneratorFrame(PythonLanguage.get(readFrameNode), info.getRootNode(), generatorFrame, self.getGeneratorFunction(), self.getGlobals(), + currentRef); } int bci = self.getBci(); if (bci >= 0) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java index b83c1d6190..c1366dd488 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/PGenerator.java @@ -42,7 +42,6 @@ import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLFrameInfo; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.runtime.PythonOptions; -import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.RootCallTarget; @@ -53,9 +52,7 @@ import com.oracle.truffle.api.bytecode.ContinuationRootNode; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.MaterializedFrame; -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.strings.TruffleString; public class PGenerator extends PythonBuiltinObject { @@ -121,6 +118,7 @@ public Object handleResult(PythonLanguage language, GeneratorYieldResult result) public static class BytecodeDSLState { private final PBytecodeDSLRootNode rootNode; private final Object[] arguments; + private ContinuationRootNode prevContinuationRootNode; private ContinuationRootNode continuationRootNode; private boolean isStarted; @@ -131,8 +129,11 @@ public BytecodeDSLState(PBytecodeDSLRootNode rootNode, Object[] arguments, Conti } public Object handleResult(PGenerator generator, ContinuationResult result) { - assert result.getContinuationRootNode() == null || result.getContinuationRootNode().getFrameDescriptor() == generator.frame.getFrameDescriptor(); + assert PBytecodeDSLRootNode.cast(result.getContinuationRootNode()).getFrameDescriptor() == generator.frame.getFrameDescriptor(); isStarted = true; + // We must keep the previous root so that we can load its BytecodeNode to resolve BCI to + // location, the next continuation node may have different BytecodeNode + prevContinuationRootNode = continuationRootNode; continuationRootNode = result.getContinuationRootNode(); return result.getResult(); } @@ -276,6 +277,10 @@ public PythonObject getGlobals() { return globals; } + public PFunction getGeneratorFunction() { + return generatorFunction; + } + public static Object getSendValue(Object[] arguments) { return PArguments.getArgument(arguments, 1); } @@ -301,12 +306,11 @@ public RootCallTarget getCurrentCallTarget() { public BytecodeNode getBytecodeNode() { assert PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER; + assert !running; // When it is running, we must use stack walking to get the location BytecodeDSLState state = getBytecodeDSLState(); if (state.isStarted) { - return state.continuationRootNode.getLocation().getBytecodeNode(); + return state.prevContinuationRootNode.getLocation().getBytecodeNode(); } else { - // Loading the BytecodeNode from the RootNode field is, in general, not correct, but can - // be used if we know that the generator function is currently not on stack. return state.rootNode.getBytecodeNode(); } } @@ -384,14 +388,6 @@ public final String toString() { return ""; } - public final PCode getOrCreateCode(Node inliningTarget, InlinedConditionProfile hasCodeProfile) { - if (hasCodeProfile.profile(inliningTarget, code == null)) { - RootCallTarget callTarget = getRootNode().getCallTarget(); - code = PFactory.createCode(PythonLanguage.get(inliningTarget), callTarget); - } - return code; - } - public final boolean isRunning() { return running; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java index 9aef20b36d..14490e2c35 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.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 @@ -60,7 +60,6 @@ import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; -import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; @@ -130,25 +129,6 @@ static TruffleString doIndexedSlotDescriptor(IndexedSlotDescriptor self) { } } - @GenerateInline - @GenerateCached(false) - @GenerateUncached - abstract static class DescriptorCheckNode extends Node { - public abstract void execute(Node inliningTarget, Object descrType, Object nameObj, Object obj); - - // https://github.com/python/cpython/blob/e8b19656396381407ad91473af5da8b0d4346e88/Objects/descrobject.c#L70 - @Specialization - static void check(Node inliningTarget, Object descrType, Object name, Object obj, - @Cached GetClassNode getClassNode, - @Cached(inline = false) IsSubtypeNode isSubtypeNode, - @Cached PRaiseNode raiseNode) { - Object type = getClassNode.execute(inliningTarget, obj); - if (!isSubtypeNode.execute(type, descrType)) { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.DESC_S_FOR_N_DOESNT_APPLY_TO_N, name, descrType, type); - } - } - } - @GenerateUncached @GenerateInline(value = false) public abstract static class DescrGetNode extends Node { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/GetSetDescriptorTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/GetSetDescriptorTypeBuiltins.java index 36c846453f..ce29b21e0c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/GetSetDescriptorTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/GetSetDescriptorTypeBuiltins.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 @@ -53,7 +53,6 @@ import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescrDeleteNode; import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescrGetNode; import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescrSetNode; -import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescriptorCheckNode; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetNameNode; @@ -61,6 +60,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet.DescrSetBuiltinNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +import com.oracle.graal.python.nodes.object.DescriptorCheckNode; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/MemberDescriptorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/MemberDescriptorBuiltins.java index 411cd2393f..65d432ccb6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/MemberDescriptorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/MemberDescriptorBuiltins.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 @@ -55,7 +55,6 @@ import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescrDeleteNode; import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescrGetNode; import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescrSetNode; -import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorBuiltins.DescriptorCheckNode; import com.oracle.graal.python.builtins.objects.object.ObjectNodes.GetIdNode; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -66,6 +65,7 @@ import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +import com.oracle.graal.python.nodes.object.DescriptorCheckNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; 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 387b08acba..477dfe3e70 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 @@ -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 @@ -81,9 +81,8 @@ 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; 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,11 +119,11 @@ 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; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; @@ -166,11 +165,10 @@ 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; +import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.profiles.InlinedIntValueProfile; @@ -211,42 +209,47 @@ static Object doGeneric(VirtualFrame frame, Object cls, Object x, Object baseObj @Bind Node inliningTarget, @Cached IntNodeInnerNode innerNode, @Cached IsBuiltinClassExactProfile isPrimitiveIntProfile, + @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, + @Cached TypeNodes.GetInstanceShape getInstanceShape, @Cached CreateIntSubclassNode createIntSubclassNode) { Object result = innerNode.execute(frame, inliningTarget, x, baseObj); if (isPrimitiveIntProfile.profileClass(inliningTarget, cls, PythonBuiltinClassType.PInt)) { return result; } else { - return createIntSubclassNode.execute(inliningTarget, cls, result); + assert IsSubtypeNode.getUncached().execute(cls, PythonBuiltinClassType.PInt); + PInt managedInt = createIntSubclassNode.execute(inliningTarget, cls, result, getInstanceShape.execute(cls)); + if (needsNativeAllocationNode.execute(inliningTarget, cls)) { + // needsNativeAllocationNode acts as branch profile + return CExtNodes.allocateNativePart(inliningTarget, cls, managedInt); + } else { + return managedInt; + } } } @GenerateInline @GenerateCached(false) abstract static class CreateIntSubclassNode extends Node { - public abstract Object execute(Node inliningTarget, Object cls, Object intObj); + public abstract PInt execute(Node inliningTarget, Object cls, Object intObj, Shape shape); @Specialization - static Object doSubclass(Object cls, int value, - @Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) { - return PFactory.createInt(cls, getInstanceShape.execute(cls), value); + static PInt doManagedSubclass(Object cls, int value, Shape shape) { + return PFactory.createInt(cls, shape, value); } @Specialization - static Object doSubclass(Object cls, long value, - @Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) { - return PFactory.createInt(cls, getInstanceShape.execute(cls), value); + static PInt doManagedSubclass(Object cls, long value, Shape shape) { + return PFactory.createInt(cls, shape, value); } @Specialization - static Object doSubclass(Object cls, boolean value, - @Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) { - return PFactory.createInt(cls, getInstanceShape.execute(cls), PInt.intValue(value)); + static PInt doManagedSubclass(Object cls, boolean value, Shape shape) { + return PFactory.createInt(cls, shape, PInt.intValue(value)); } @Specialization - static Object doSubclass(Object cls, PInt value, - @Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) { - return PFactory.createInt(cls, getInstanceShape.execute(cls), value.getValue()); + static PInt doManagedSubclass(Object cls, PInt value, Shape shape) { + return PFactory.createInt(cls, shape, value.getValue()); } } @@ -1540,8 +1543,9 @@ static long ceil(long arg) { } @Specialization - static PInt ceil(PInt arg) { - return arg; + static PInt ceil(PInt arg, + @Bind PythonLanguage language) { + return PFactory.createInt(language, arg.getValue()); } } @@ -2062,53 +2066,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) { @@ -2290,113 +2247,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) { @@ -2484,25 +2334,29 @@ public static byte[] fromLong(long self, int byteCount, boolean isBigEndian, boo byte[] bytes = new byte[byteCount]; long number = self; - while (number != 0 && 0 <= index && index <= (byteCount - 1)) { + while (number != signByte && 0 <= index && index <= (byteCount - 1)) { bytes[index] = (byte) (number & 0xFF); - if (number == signByte) { - number = 0; - } number >>= 8; index += delta; } - if (overflowProfile.profile(inliningTarget, !signed && number != 0 || (signed && bytes.length == 1 && bytes[0] != self) || (byteCount == 0 && self != 0 && self != -1))) { - throw raiseNode.raise(inliningTarget, PythonErrorType.OverflowError, ErrorMessages.MESSAGE_INT_TO_BIG); - } - if (signed) { while (0 <= index && index <= (byteCount - 1)) { bytes[index] = signByte; index += delta; } } + + boolean overflow = number != signByte; + if (signed && byteCount > 0) { + int mostSignificantByte = isBigEndian ? bytes[0] : bytes[byteCount - 1]; + if ((mostSignificantByte < 0) != (self < 0)) { + overflow = true; + } + } + if (overflowProfile.profile(inliningTarget, overflow)) { + throw raiseNode.raise(inliningTarget, PythonErrorType.OverflowError, ErrorMessages.MESSAGE_INT_TO_BIG); + } return bytes; } @@ -2631,14 +2485,14 @@ static Object fromObject(VirtualFrame frame, Object cl, Object object, TruffleSt throw raiseNode.raise(inliningTarget, PythonErrorType.ValueError, ErrorMessages.BYTEORDER_MUST_BE_LITTLE_OR_BIG); } byte[] bytes; - Object bytesObj = callBytes.executeObject(frame, object); - if (bytesObj != PNone.NO_VALUE) { + try { + Object bytesObj = callBytes.executeObject(frame, object); hasBytesProfile.enter(inliningTarget); if (!(bytesObj instanceof PBytes)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.RETURNED_NONBYTES, T___BYTES__); } bytes = bufferLib.getCopiedByteArray(bytesObj); - } else { + } catch (SpecialMethodNotFound e) { bytes = bytesFromObject.execute(frame, object); } Object result = fromByteArray.execute(inliningTarget, bytes, littleEndian, signed); @@ -2678,12 +2532,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) @@ -2693,7 +2541,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); } @@ -2742,14 +2590,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) @@ -2881,20 +2721,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 0c559e09e8..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 @@ -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.PIterator; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___LENGTH_HINT__; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; @@ -56,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; @@ -111,11 +113,17 @@ public abstract class IteratorNodes { */ @GenerateInline @GenerateCached(false) + @GenerateUncached @ImportStatic({PGuards.class, SpecialMethodNames.class}) 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 @@ -202,7 +210,7 @@ static int length(VirtualFrame frame, Node inliningTarget, Object iterable, if (indexCheckNode.execute(inliningTarget, len)) { int intLen = asSizeNode.executeExact(frame, inliningTarget, len); if (intLen < 0) { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.LENGTH_HINT_SHOULD_RETURN_MT_ZERO); + throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.LENGTH_HINT_SHOULD_RETURN_MT_ZERO); } return intLen; } else { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/IsliceBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/IsliceBuiltins.java index 10f0c7ce3f..27f434159b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/IsliceBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/IsliceBuiltins.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 @@ -157,10 +157,10 @@ static Object constructOne(VirtualFrame frame, Object cls, Object[] args, PKeywo stopNotInt.enter(inliningTarget); throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Indices"); } - } - if (stop < -1 || stop > SysModuleBuiltins.MAXSIZE) { - stopWrongValue.enter(inliningTarget); - throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Indices"); + if (stop < 0 || stop > SysModuleBuiltins.MAXSIZE) { + stopWrongValue.enter(inliningTarget); + throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Indices"); + } } } else if (argsLen2.profile(inliningTarget, args.length == 3) || argsLen3.profile(inliningTarget, args.length == 4)) { if (args[1] != PNone.NONE) { @@ -180,6 +180,10 @@ static Object constructOne(VirtualFrame frame, Object cls, Object[] args, PKeywo stopNotInt.enter(inliningTarget); throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Stop argument"); } + if (stop < 0 || stop > SysModuleBuiltins.MAXSIZE) { + wrongValue.enter(inliningTarget); + throw raiseNode.raise(inliningTarget, ValueError, S_FOR_ISLICE_MUST_BE, "Indices"); + } } if (start < 0 || stop < -1 || start > SysModuleBuiltins.MAXSIZE || stop > SysModuleBuiltins.MAXSIZE) { wrongValue.enter(inliningTarget); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/ProductBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/ProductBuiltins.java index 261beced0f..201ba903be 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/ProductBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/itertools/ProductBuiltins.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,9 +41,11 @@ package com.oracle.graal.python.builtins.objects.itertools; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.nodes.ErrorMessages.ARG_CANNOT_BE_NEGATIVE; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___SETSTATE__; +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SETSTATE__; import java.util.List; @@ -59,21 +61,25 @@ import com.oracle.graal.python.builtins.modules.ItertoolsModuleBuiltins.DeprecatedSetStateBuiltin; import com.oracle.graal.python.builtins.objects.PNone; 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.tuple.PTuple; 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.slots.TpSlotIterNext.TpIterNextBuiltin; -import com.oracle.graal.python.lib.PyLongAsIntNode; +import com.oracle.graal.python.lib.PyNumberAsSizeNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.PyTupleGetItem; +import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.util.CannotCastException; +import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.PythonUtils; +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; @@ -102,7 +108,7 @@ protected List> getNodeFa @GenerateNodeFactory public abstract static class ProductNode extends PythonBuiltinNode { - @Specialization(guards = "isTypeNode.execute(inliningTarget, cls)") + @Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "isNoValue(repeat)"}) static Object constructNoneRepeat(VirtualFrame frame, Object cls, Object[] iterables, @SuppressWarnings("unused") PNone repeat, @SuppressWarnings("unused") @Bind Node inliningTarget, @Cached.Shared @Cached IteratorNodes.ToArrayNode toArrayNode, @@ -113,57 +119,47 @@ static Object constructNoneRepeat(VirtualFrame frame, Object cls, Object[] itera return self; } - @Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "repeat == 1"}, limit = "1") - static Object constructOneRepeat(VirtualFrame frame, Object cls, Object[] iterables, @SuppressWarnings("unused") int repeat, - @SuppressWarnings("unused") @Bind Node inliningTarget, - @Cached.Shared @Cached IteratorNodes.ToArrayNode toArrayNode, - @SuppressWarnings("unused") @Exclusive @Cached TypeNodes.IsTypeNode isTypeNode, - @Cached.Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) { - PProduct self = PFactory.createProduct(cls, getInstanceShape.execute(cls)); - constructOneRepeat(frame, self, iterables, toArrayNode); - return self; - } - - @Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "repeat > 1"}, limit = "1") - static Object construct(VirtualFrame frame, Object cls, Object[] iterables, int repeat, + @Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "!isNoValue(repeat)"}, limit = "1") + static Object constructRepeat(VirtualFrame frame, Object cls, Object[] iterables, Object repeat, @Bind Node inliningTarget, + @Exclusive @Cached PyNumberAsSizeNode asSizeNode, @Cached.Shared @Cached IteratorNodes.ToArrayNode toArrayNode, + @Cached InlinedBranchProfile errorProfile, + @Cached InlinedBranchProfile zeroProfile, + @Cached InlinedBranchProfile oneProfile, + @Cached InlinedBranchProfile genericProfile, @Cached InlinedLoopConditionProfile loopProfile, @SuppressWarnings("unused") @Exclusive @Cached TypeNodes.IsTypeNode isTypeNode, @Cached.Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) { - Object[][] lists = unpackIterables(frame, iterables, toArrayNode); - Object[][] gears = new Object[lists.length * repeat][]; - loopProfile.profileCounted(inliningTarget, repeat); - LoopNode.reportLoopCount(inliningTarget, repeat); - for (int i = 0; loopProfile.inject(inliningTarget, i < repeat); i++) { - PythonUtils.arraycopy(lists, 0, gears, i * lists.length, lists.length); + int repeatInt = asSizeNode.executeExact(frame, inliningTarget, repeat); + if (repeatInt < 0) { + errorProfile.enter(inliningTarget); + throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ARG_CANNOT_BE_NEGATIVE, "repeat"); } PProduct self = PFactory.createProduct(cls, getInstanceShape.execute(cls)); - construct(self, gears); - return self; - } - - @Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "repeat == 0"}, limit = "1") - static Object constructNoRepeat(Object cls, @SuppressWarnings("unused") Object[] iterables, @SuppressWarnings("unused") int repeat, - @SuppressWarnings("unused") @Bind Node inliningTarget, - @SuppressWarnings("unused") @Exclusive @Cached TypeNodes.IsTypeNode isTypeNode, - @Cached.Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) { - PProduct self = PFactory.createProduct(cls, getInstanceShape.execute(cls)); - self.setGears(new Object[0][]); - self.setIndices(new int[0]); - self.setLst(null); - self.setStopped(false); + if (repeatInt == 0) { + zeroProfile.enter(inliningTarget); + self.setGears(new Object[0][]); + self.setIndices(new int[0]); + self.setLst(null); + self.setStopped(false); + } else if (repeatInt == 1) { + oneProfile.enter(inliningTarget); + constructOneRepeat(frame, self, iterables, toArrayNode); + } else { + genericProfile.enter(inliningTarget); + Object[][] lists = unpackIterables(frame, iterables, toArrayNode); + Object[][] gears = new Object[lists.length * repeatInt][]; + loopProfile.profileCounted(inliningTarget, repeatInt); + LoopNode.reportLoopCount(inliningTarget, repeatInt); + for (int i = 0; loopProfile.inject(inliningTarget, i < repeatInt); i++) { + PythonUtils.arraycopy(lists, 0, gears, i * lists.length, lists.length); + } + construct(self, gears); + } return self; } - @SuppressWarnings("unused") - @Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "repeat < 0"}, limit = "1") - static Object constructNeg(Object cls, Object[] iterables, int repeat, - @Bind Node inliningTarget, - @Exclusive @Cached TypeNodes.IsTypeNode isTypeNode) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ARG_CANNOT_BE_NEGATIVE, "repeat"); - } - private static void constructOneRepeat(VirtualFrame frame, PProduct self, Object[] iterables, IteratorNodes.ToArrayNode toArrayNode) { Object[][] gears = unpackIterables(frame, iterables, toArrayNode); construct(self, gears); @@ -323,11 +319,11 @@ static Object reduce(PProduct self) { } private static PTuple createGearTuple(PProduct self, PythonLanguage language) { - PList[] lists = new PList[self.getGears().length]; - for (int i = 0; i < lists.length; i++) { - lists[i] = PFactory.createList(language, self.getGears()[i]); + PTuple[] tuples = new PTuple[self.getGears().length]; + for (int i = 0; i < tuples.length; i++) { + tuples[i] = PFactory.createTuple(language, self.getGears()[i]); } - return PFactory.createTuple(language, lists); + return PFactory.createTuple(language, tuples); } } @@ -335,25 +331,34 @@ private static PTuple createGearTuple(PProduct self, PythonLanguage language) { @GenerateNodeFactory public abstract static class SetStateNode extends DeprecatedSetStateBuiltin { @Specialization - static Object setState(PProduct self, Object state) { + @TruffleBoundary + static Object setState(PProduct self, Object state, + @Bind Node inliningTarget) { Object[][] gears = self.getGears(); + if (!PyTupleCheckNode.executeUncached(state) || PyTupleSizeNode.executeUncached(state) != gears.length) { + throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ErrorMessages.INVALID_ARGS, T___SETSTATE__); + } Object[] lst = new Object[gears.length]; int[] indices = self.getIndices(); - for (int i = 0; i < gears.length; i++) { - Object o = PyTupleGetItem.executeUncached(state, i); - int index = PyLongAsIntNode.executeUncached(o); - int gearSize = gears[i].length; - if (indices == null || gearSize == 0) { - self.setStopped(true); - return PNone.NONE; - } - if (index < 0) { - index = 0; - } else if (index > gearSize - 1) { - index = gearSize - 1; + try { + for (int i = 0; i < gears.length; i++) { + Object o = PyTupleGetItem.executeUncached(state, i); + int index = CastToJavaIntExactNode.executeUncached(o); + int gearSize = gears[i].length; + if (indices == null || gearSize == 0) { + self.setStopped(true); + return PNone.NONE; + } + if (index < 0) { + index = 0; + } else if (index > gearSize - 1) { + index = gearSize - 1; + } + indices[i] = index; + lst[i] = gears[i][index]; } - indices[i] = index; - lst[i] = gears[i][index]; + } catch (CannotCastException e) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.INTEGER_REQUIRED); } self.setLst(lst); return PNone.NONE; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/list/PList.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/list/PList.java index 45c98c2eaa..fd5f3de4fd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/list/PList.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/list/PList.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. @@ -47,13 +47,11 @@ 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.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.object.Shape; import com.oracle.truffle.api.profiles.InlinedBranchProfile; -import com.oracle.truffle.api.source.SourceSection; @SuppressWarnings("truffle-abstract-export") @ExportLibrary(InteropLibrary.class) @@ -82,29 +80,6 @@ public ListOrigin getOrigin() { return origin; } - @ExportMessage - public SourceSection getSourceLocation(@Exclusive @Cached GilNode gil) throws UnsupportedMessageException { - boolean mustRelease = gil.acquire(); - try { - ListOrigin node = getOrigin(); - SourceSection result = null; - if (node != null) { - result = node.getSourceSection(); - } - if (result == null) { - throw UnsupportedMessageException.create(); - } - return result; - } finally { - gil.release(mustRelease); - } - } - - @ExportMessage - public boolean hasSourceLocation() { - return getOrigin() != null && getOrigin().getSourceSection() != null; - } - @ExportMessage public boolean isArrayElementModifiable(long index, @Exclusive @Cached IndexNodes.NormalizeIndexCustomMessageNode normalize, @@ -235,7 +210,5 @@ public int updateFrom(int newSizeEstimate) { } void reportUpdatedCapacity(ArrayBasedSequenceStorage newStore); - - SourceSection getSourceSection(); } } 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..6fb2524ae9 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; } @@ -239,7 +238,8 @@ public void setCachedHash(int cachedHash) { public void setReleased() { flags |= FLAG_RELEASED; if (reference != null) { - reference.markReleased(); + boolean markedReleased = reference.markReleased(); + assert markedReleased || reference.isReleased(); reference = null; } buffer = null; @@ -473,7 +473,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 +481,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 9b7de8fadc..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 @@ -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. @@ -55,10 +55,8 @@ import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.module.PythonModule; -import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.tuple.PTuple; 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.slots.TpSlotHashFun.HashBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; import com.oracle.graal.python.lib.PyObjectGetAttr; @@ -76,7 +74,6 @@ import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode; -import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; @@ -84,7 +81,6 @@ 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.CompilerDirectives; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; @@ -93,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; @@ -148,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; } @@ -228,9 +217,9 @@ static Object getModule(VirtualFrame frame, PBuiltinMethod self, @SuppressWarnin @Bind Node inliningTarget, @Cached("createFor($node)") BoundaryCallData boundaryCallData, @Cached PyObjectLookupAttr lookup, - @Cached ReadAttributeFromPythonObjectNode readAttrNode) { + @Cached(inline = true) ReadAttributeFromPythonObjectNode readAttrNode) { // No profiling, performance here is not very important - Object module = readAttrNode.execute(self, T___MODULE__, PNone.NO_VALUE); + Object module = readAttrNode.execute(inliningTarget, self, T___MODULE__, PNone.NO_VALUE); if (module != PNone.NO_VALUE) { return module; } @@ -285,105 +274,44 @@ static Object getDoc(PBuiltinMethod self, } } + static TruffleString getFunctionAttr(VirtualFrame frame, Object self, Node inliningTarget, CastToTruffleStringNode toStringNode, PyObjectGetAttr getAttr, PRaiseNode raiseNode, + TruffleString attrName) { + Object function; + if (self instanceof PMethod method) { + function = method.getFunction(); + } else { + function = ((PBuiltinMethod) self).getFunction(); + } + try { + return toStringNode.execute(inliningTarget, getAttr.execute(frame, inliningTarget, function, attrName)); + } catch (CannotCastException cce) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.IS_NOT_A_UNICODE_OBJECT, attrName); + } + } + @Builtin(name = J___NAME__, minNumOfPositionalArgs = 1, isGetter = true) @GenerateNodeFactory public abstract static class NameNode extends PythonUnaryBuiltinNode { @Specialization - static Object getName(VirtualFrame frame, PBuiltinMethod method, - @Bind Node inliningTarget, - @Shared("toStringNode") @Cached CastToTruffleStringNode toStringNode, - @Shared("getAttr") @Cached PyObjectGetAttr getAttr) { - try { - return toStringNode.execute(inliningTarget, getAttr.execute(frame, inliningTarget, method.getFunction(), T___NAME__)); - } catch (CannotCastException cce) { - throw CompilerDirectives.shouldNotReachHere(); - } - } - - @Specialization - static Object getName(VirtualFrame frame, PMethod method, + static TruffleString getName(VirtualFrame frame, Object self, @Bind Node inliningTarget, - @Shared("toStringNode") @Cached CastToTruffleStringNode toStringNode, - @Shared("getAttr") @Cached PyObjectGetAttr getAttr) { - try { - return toStringNode.execute(inliningTarget, getAttr.execute(frame, inliningTarget, method.getFunction(), T___NAME__)); - } catch (CannotCastException cce) { - throw CompilerDirectives.shouldNotReachHere(); - } + @Cached CastToTruffleStringNode toStringNode, + @Cached PyObjectGetAttr getAttr, + @Cached PRaiseNode raiseNode) { + return getFunctionAttr(frame, self, inliningTarget, toStringNode, getAttr, raiseNode, T___NAME__); } } @Builtin(name = J___QUALNAME__, minNumOfPositionalArgs = 1, isGetter = true) @GenerateNodeFactory public abstract static class QualNameNode extends PythonUnaryBuiltinNode { - - protected static boolean isSelfModuleOrNull(PMethod method) { - return method.getSelf() == PNone.NO_VALUE || PGuards.isPythonModule(method.getSelf()); - } - - protected static boolean isSelfModuleOrNull(PBuiltinMethod method) { - return method.getSelf() == PNone.NO_VALUE || PGuards.isPythonModule(method.getSelf()); - } - - @Specialization(guards = "isSelfModuleOrNull(method)") - static TruffleString doSelfIsModule(VirtualFrame frame, PMethod method, - @Bind Node inliningTarget, - @Shared("toStringNode") @Cached CastToTruffleStringNode toStringNode, - @Shared("lookupName") @Cached PyObjectLookupAttr lookupName) { - return getName(frame, inliningTarget, method.getFunction(), toStringNode, lookupName); - } - - @Specialization(guards = "isSelfModuleOrNull(method)") - static TruffleString doSelfIsModule(VirtualFrame frame, PBuiltinMethod method, - @Bind Node inliningTarget, - @Shared("toStringNode") @Cached CastToTruffleStringNode toStringNode, - @Shared("lookupName") @Cached PyObjectLookupAttr lookupName) { - return getName(frame, inliningTarget, method.getFunction(), toStringNode, lookupName); - } - - @Specialization(guards = "!isSelfModuleOrNull(method)") - static TruffleString doSelfIsObject(VirtualFrame frame, PMethod method, - @Bind Node inliningTarget, - @Shared @Cached GetClassNode getClassNode, - @Shared @Cached TypeNodes.IsTypeNode isTypeNode, - @Shared("toStringNode") @Cached CastToTruffleStringNode toStringNode, - @Shared("getQualname") @Cached PyObjectGetAttr getQualname, - @Shared("lookupName") @Cached PyObjectLookupAttr lookupName, - @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode, - @Shared @Cached PRaiseNode raiseNode) { - return getQualName(frame, inliningTarget, method.getSelf(), method.getFunction(), getClassNode, isTypeNode, toStringNode, getQualname, lookupName, simpleTruffleStringFormatNode, - raiseNode); - } - - @Specialization(guards = "!isSelfModuleOrNull(method)") - static TruffleString doSelfIsObject(VirtualFrame frame, PBuiltinMethod method, + @Specialization + static TruffleString getQualname(VirtualFrame frame, Object self, @Bind Node inliningTarget, - @Shared @Cached GetClassNode getClassNode, - @Shared @Cached TypeNodes.IsTypeNode isTypeNode, - @Shared("toStringNode") @Cached CastToTruffleStringNode toStringNode, - @Shared("getQualname") @Cached PyObjectGetAttr getQualname, - @Shared("lookupName") @Cached PyObjectLookupAttr lookupName, - @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode, - @Shared @Cached PRaiseNode raiseNode) { - return getQualName(frame, inliningTarget, method.getSelf(), method.getFunction(), getClassNode, isTypeNode, toStringNode, getQualname, lookupName, simpleTruffleStringFormatNode, - raiseNode); - } - - private static TruffleString getQualName(VirtualFrame frame, Node inliningTarget, Object self, Object func, GetClassNode getClassNode, TypeNodes.IsTypeNode isTypeNode, - CastToTruffleStringNode toStringNode, PyObjectGetAttr getQualname, PyObjectLookupAttr lookupName, SimpleTruffleStringFormatNode simpleTruffleStringFormatNode, - PRaiseNode raiseNode) { - Object type = isTypeNode.execute(inliningTarget, self) ? self : getClassNode.execute(inliningTarget, self); - - try { - TruffleString typeQualName = toStringNode.execute(inliningTarget, getQualname.execute(frame, inliningTarget, type, T___QUALNAME__)); - return simpleTruffleStringFormatNode.format("%s.%s", typeQualName, getName(frame, inliningTarget, func, toStringNode, lookupName)); - } catch (CannotCastException cce) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.IS_NOT_A_UNICODE_OBJECT, T___QUALNAME__); - } - } - - private static TruffleString getName(VirtualFrame frame, Node inliningTarget, Object func, CastToTruffleStringNode toStringNode, PyObjectLookupAttr lookupName) { - return toStringNode.execute(inliningTarget, lookupName.execute(frame, inliningTarget, func, T___NAME__)); + @Cached CastToTruffleStringNode toStringNode, + @Cached PyObjectGetAttr getQualname, + @Cached PRaiseNode raiseNode) { + return getFunctionAttr(frame, self, inliningTarget, toStringNode, getQualname, raiseNode, T___QUALNAME__); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinClassmethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinClassmethodBuiltins.java index 34a8e80445..9dc0b3832e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinClassmethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinClassmethodBuiltins.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 @@ -61,21 +61,35 @@ 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.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; 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.slots.TpSlotDescrGet.DescrGetBuiltinNode; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; +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.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; +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.Bind; import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Cached.Exclusive; import com.oracle.truffle.api.dsl.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.dsl.ReportPolymorphism; 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.InlinedBranchProfile; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.PBuiltinClassMethod) @@ -88,6 +102,97 @@ protected List> getNodeFa return BuiltinClassmethodBuiltinsFactory.getFactories(); } + @Slot(SlotKind.tp_descr_get) + @ReportPolymorphism + @GenerateUncached + @GenerateNodeFactory + abstract static class GetNode extends DescrGetBuiltinNode { + // If self.getCallable() is null, let the next @Specialization handle that + @Specialization(guards = {"isSingleContext()", "isNoValue(type) == typeIsNoValue", "cachedSelf == self", "cachedCallable != null"}, limit = "3") + static Object getCached(@SuppressWarnings("unused") PDecoratedMethod self, Object obj, Object type, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached(value = "self", weak = true) PDecoratedMethod cachedSelf, + @Cached(value = "self.getCallable()", weak = true) Object cachedCallable, + @Cached("isNoValue(type)") boolean typeIsNoValue, + @Exclusive @Cached GetClassNode getClass, + @Exclusive @Cached IsSubtypeNode isSubtypeNode, + @Exclusive @Cached InlinedBranchProfile errorProfile, + @Exclusive @Cached ClassmethodCommonBuiltins.MakeMethodNode makeMethod) { + Object actualType = getType(inliningTarget, errorProfile, getClass, cachedCallable, typeIsNoValue, obj, type); + return doGet(inliningTarget, isSubtypeNode, errorProfile, makeMethod, actualType, cachedCallable); + } + + @InliningCutoff + @Specialization(replaces = "getCached") + static Object get(PDecoratedMethod self, Object obj, Object type, + @Bind Node inliningTarget, + @Exclusive @Cached GetClassNode getClass, + @Exclusive @Cached IsSubtypeNode isSubtypeNode, + @Exclusive @Cached InlinedBranchProfile errorProfile, + @Exclusive @Cached ClassmethodCommonBuiltins.MakeMethodNode makeMethod, + @Exclusive @Cached PRaiseNode raiseNode) { + Object callable = ClassmethodCommonBuiltins.getCallable(inliningTarget, self, raiseNode); + Object actualType = getType(inliningTarget, errorProfile, getClass, callable, PGuards.isNoValue(type), obj, type); + return doGet(inliningTarget, isSubtypeNode, errorProfile, makeMethod, actualType, callable); + } + + private static Object getType(Node inliningTarget, InlinedBranchProfile errorProfile, GetClassNode getClassNode, + Object callable, boolean typeIsNoValue, Object obj, Object type) { + if (typeIsNoValue) { + if (PGuards.isNoValue(obj)) { + errorProfile.enter(inliningTarget); + throw raiseNeedsEitherObjOrType(inliningTarget, callable); + } + return getClassNode.execute(inliningTarget, obj); + } + return type; + } + + @TruffleBoundary + private static PException raiseNeedsEitherObjOrType(Node inliningTarget, Object callable) { + if (callable instanceof PBuiltinFunction pbf) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, + ErrorMessages.DESCRIPTOR_S_FOR_TYPE_S_NEEDS_EITHER_OBJ_OR_TYPE, pbf.getName(), + TypeNodes.GetNameNode.executeUncached(pbf.getEnclosingType())); + } else { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError); + } + } + + private static Object doGet(Node inliningTarget, IsSubtypeNode isSubtypeNode, InlinedBranchProfile errorProfile, + ClassmethodCommonBuiltins.MakeMethodNode makeMethod, Object type, Object callable) { + if (!PGuards.isPythonClass(type)) { + errorProfile.enter(inliningTarget); + throw raiseNeedsType(inliningTarget, callable, type); + } + // Not clear if we can get any other callable than PBuiltinFunction... + if (callable instanceof PBuiltinFunction builtinFunction) { + Object descriptorType = builtinFunction.getEnclosingType(); + if (!isSubtypeNode.execute(type, descriptorType)) { + errorProfile.enter(inliningTarget); + throw raiseRequiresSubtype(inliningTarget, builtinFunction, descriptorType, type); + } + } + return makeMethod.execute(inliningTarget, type, callable); + } + + @TruffleBoundary + private static RuntimeException raiseNeedsType(Node inliningTarget, Object callable, Object type) { + if (callable instanceof PBuiltinFunction pbf) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.DESCRIPTOR_S_FOR_TYPE_S_NEEDS_TYPE_NOT_P_AS_ARG_2, + pbf.getName(), TypeNodes.GetNameNode.executeUncached(pbf.getEnclosingType()), type); + } else { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError); + } + } + + @TruffleBoundary + private static RuntimeException raiseRequiresSubtype(Node inliningTarget, PBuiltinFunction builtinFunction, Object descriptorType, Object type) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.DESCRIPTOR_S_REQUIRES_SUBTYPE_OF_S_BUT_RECEIVED_S, + builtinFunction.getName(), TypeNodes.GetNameNode.executeUncached(descriptorType), TypeNodes.GetNameNode.executeUncached(type)); + } + } + @Builtin(name = J___NAME__, maxNumOfPositionalArgs = 1, isGetter = true) @GenerateNodeFactory abstract static class NameNode extends PythonUnaryBuiltinNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/ClassmethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/ClassmethodBuiltins.java index 36aa0478a9..c6c0b5854f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/ClassmethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/ClassmethodBuiltins.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 @@ -54,18 +54,29 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; 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; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlot; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; import com.oracle.graal.python.lib.PyObjectSetAttr; +import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; 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.object.PFactory; +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.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.NodeFactory; +import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; @@ -109,6 +120,55 @@ protected PNone init(VirtualFrame frame, PDecoratedMethod self, Object callable, } } + @Slot(value = SlotKind.tp_descr_get, isComplex = true) + @ReportPolymorphism + @GenerateUncached + @GenerateNodeFactory + abstract static class GetNode extends DescrGetBuiltinNode { + // If self.getCallable() is null, let the next @Specialization handle that + @Specialization(guards = {"isSingleContext()", "isNoValue(type) == typeIsNoValue", "cachedSelf == self", "cachedCallable != null"}, limit = "3") + static Object getCached(VirtualFrame frame, @SuppressWarnings("unused") PDecoratedMethod self, Object obj, Object type, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached(value = "self", weak = true) PDecoratedMethod cachedSelf, + @Cached(value = "self.getCallable()", weak = true) Object cachedCallable, + @Cached("isNoValue(type)") boolean typeIsNoValue, + @Exclusive @Cached GetClassNode getClass, + @Exclusive @Cached GetObjectSlotsNode getCallableSlots, + @Exclusive @Cached CallSlotDescrGet callGet, + @Exclusive @Cached ClassmethodCommonBuiltins.MakeMethodNode makeMethod) { + Object actualType = typeIsNoValue ? getClass.execute(inliningTarget, obj) : type; + return doGet(frame, inliningTarget, actualType, cachedCallable, getCallableSlots, callGet, makeMethod); + } + + @InliningCutoff + @Specialization(replaces = "getCached") + static Object get(VirtualFrame frame, PDecoratedMethod self, Object obj, Object type, + @Bind Node inliningTarget, + @Exclusive @Cached GetClassNode getClass, + @Exclusive @Cached GetObjectSlotsNode getCallableSlots, + @Exclusive @Cached CallSlotDescrGet callGet, + @Exclusive @Cached ClassmethodCommonBuiltins.MakeMethodNode makeMethod, + @Exclusive @Cached PRaiseNode raiseNode) { + Object actualType = PGuards.isNoValue(type) ? getClass.execute(inliningTarget, obj) : type; + return doGet(frame, inliningTarget, actualType, ClassmethodCommonBuiltins.getCallable(inliningTarget, self, raiseNode), + getCallableSlots, callGet, makeMethod); + } + + private static Object doGet(VirtualFrame frame, Node inliningTarget, Object type, Object callable, + GetObjectSlotsNode getCallableSlots, CallSlotDescrGet callGet, ClassmethodCommonBuiltins.MakeMethodNode makeMethod) { + TpSlot get = getCallableSlots.execute(inliningTarget, callable).tp_descr_get(); + if (get != null) { + return callGet(frame, inliningTarget, type, callable, callGet, get); + } + return makeMethod.execute(inliningTarget, type, callable); + } + + @InliningCutoff + private static Object callGet(VirtualFrame frame, Node inliningTarget, Object type, Object callable, CallSlotDescrGet callGet, TpSlot get) { + return callGet.execute(frame, inliningTarget, get, callable, type, type); + } + } + @Slot(value = SlotKind.tp_repr, isComplex = true) @GenerateNodeFactory abstract static class ReprNode extends PythonUnaryBuiltinNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/ClassmethodCommonBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/ClassmethodCommonBuiltins.java index d733da5541..5f7ff095ba 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/ClassmethodCommonBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/ClassmethodCommonBuiltins.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 @@ -53,18 +53,14 @@ import com.oracle.graal.python.builtins.objects.function.PFunction; 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.slots.TpSlotDescrGet.DescrGetBuiltinNode; 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.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode; -import com.oracle.graal.python.nodes.object.GetClassNode; 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.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -86,66 +82,12 @@ protected List> getNodeFa return ClassmethodCommonBuiltinsFactory.getFactories(); } - @Slot(SlotKind.tp_descr_get) - @ReportPolymorphism - @GenerateUncached - @GenerateNodeFactory - abstract static class GetNode extends DescrGetBuiltinNode { - /*- - TODO: (GR-53082) this is not handling following code-path added to CPython at some later point: - if (Py_TYPE(cm->cm_callable)->tp_descr_get != NULL) { - return Py_TYPE(cm->cm_callable)->tp_descr_get(cm->cm_callable, type, type); - } - - Additionally, in CPython tp_descrget is not shared between classmethod_descriptor and classmethod, - we should investigate if we can really share the implementation - */ - - // If self.getCallable() is null, let the next @Specialization handle that - @Specialization(guards = {"isSingleContext()", "isNoValue(type)", "cachedSelf == self", "cachedCallable != null"}, limit = "3") - static Object getCached(@SuppressWarnings("unused") PDecoratedMethod self, Object obj, @SuppressWarnings("unused") Object type, - @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached(value = "self", weak = true) PDecoratedMethod cachedSelf, - @SuppressWarnings("unused") @Cached(value = "self.getCallable()", weak = true) Object cachedCallable, - @Shared @Cached GetClassNode getClass, - @Shared @Cached MakeMethodNode makeMethod) { - return makeMethod.execute(inliningTarget, getClass.execute(inliningTarget, obj), cachedCallable); - } - - @Specialization(guards = "isNoValue(type)", replaces = "getCached") - static Object get(PDecoratedMethod self, Object obj, @SuppressWarnings("unused") Object type, - @Bind Node inliningTarget, - @Shared @Cached GetClassNode getClass, - @Shared @Cached MakeMethodNode makeMethod, - @Shared @Cached PRaiseNode raiseNode) { - return doGet(inliningTarget, self, getClass.execute(inliningTarget, obj), makeMethod, raiseNode); - } - - // If self.getCallable() is null, let the next @Specialization handle that - @Specialization(guards = {"isSingleContext()", "!isNoValue(type)", "cachedSelf == self", "cachedCallable != null"}, limit = "3") - static Object getTypeCached(@SuppressWarnings("unused") PDecoratedMethod self, @SuppressWarnings("unused") Object obj, Object type, - @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached(value = "self", weak = true) PDecoratedMethod cachedSelf, - @SuppressWarnings("unused") @Cached(value = "self.getCallable()", weak = true) Object cachedCallable, - @Shared @Cached MakeMethodNode makeMethod) { - return makeMethod.execute(inliningTarget, type, cachedCallable); - } - - @Specialization(guards = "!isNoValue(type)", replaces = "getTypeCached") - static Object getType(PDecoratedMethod self, @SuppressWarnings("unused") Object obj, Object type, - @Bind Node inliningTarget, - @Shared @Cached MakeMethodNode makeMethod, - @Shared @Cached PRaiseNode raiseNode) { - return doGet(inliningTarget, self, type, makeMethod, raiseNode); - } - - private static Object doGet(Node inliningTarget, PDecoratedMethod self, Object type, MakeMethodNode makeMethod, PRaiseNode raiseNode) { - Object callable = self.getCallable(); - if (callable == null) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.RuntimeError, ErrorMessages.UNINITIALIZED_S_OBJECT); - } - return makeMethod.execute(inliningTarget, type, callable); + static Object getCallable(Node inliningTarget, PDecoratedMethod self, PRaiseNode raiseNode) { + Object callable = self.getCallable(); + if (callable == null) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.RuntimeError, ErrorMessages.UNINITIALIZED_S_OBJECT); } + return callable; } @GenerateInline diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.java index e190b6b021..3788334ea9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodBuiltins.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. @@ -51,10 +51,11 @@ 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; -import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; +import com.oracle.graal.python.builtins.objects.str.StringNodes; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.type.TpSlots; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode; +import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.GetAttrBuiltinNode; import com.oracle.graal.python.lib.PyCallableCheckNode; import com.oracle.graal.python.lib.PyObjectGetAttr; @@ -63,23 +64,21 @@ 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.LookupAttributeInMRONode; import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetDefaultsNode; -import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetKeywordDefaultsNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; -import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +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.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; 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.GenerateNodeFactory; -import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; @@ -164,25 +163,22 @@ public abstract static class GetattributeNode extends GetAttrBuiltinNode { @Specialization static Object doIt(VirtualFrame frame, PMethod self, Object keyObj, @Bind Node inliningTarget, - @Cached ObjectBuiltins.GetAttributeNode objectGetattrNode, - @Cached IsBuiltinObjectProfile errorProfile, - @Cached CastToTruffleStringNode castKeyToStringNode, - @Cached PRaiseNode raiseNode) { - // TODO: (GR-53090) this is different to what CPython does and CPython also does not - // define tp_descrget for method - TruffleString key; - try { - key = castKeyToStringNode.execute(inliningTarget, keyObj); - } catch (CannotCastException e) { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); - } - - try { - return objectGetattrNode.execute(frame, self, key); - } catch (PException e) { - e.expectAttributeError(inliningTarget, errorProfile); - return objectGetattrNode.execute(frame, self.getFunction(), key); + @Cached StringNodes.CastToTruffleStringChecked1Node castToString, + @Cached GetClassNode.GetPythonObjectClassNode getClassNode, + @Cached LookupAttributeInMRONode.Dynamic lookup, + @Cached GetObjectSlotsNode getDescrSlotsNode, + @Cached CallSlotDescrGet callSlotDescrGet, + @Cached PyObjectGetAttr getAttrNode) { + TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); + Object type = getClassNode.execute(inliningTarget, self); + Object descr = lookup.execute(type, key); + if (!PGuards.isNoValue(descr)) { + TpSlots descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); + if (descrSlots.tp_descr_get() != null) { + return callSlotDescrGet.execute(frame, inliningTarget, descrSlots.tp_descr_get(), descr, self, type); + } } + return getAttrNode.execute(frame, inliningTarget, self.function, key); } @Specialization(guards = "!isPMethod(self)") @@ -237,21 +233,14 @@ static Object defaults(PMethod self, public abstract static class GetMethodKwdefaultsNode extends PythonUnaryBuiltinNode { @Specialization static Object kwDefaults(PMethod self, - @Bind Node inliningTarget, - @Cached GetKeywordDefaultsNode getKeywordDefaultsNode) { - PKeyword[] kwdefaults = getKeywordDefaultsNode.execute(inliningTarget, self); + @Bind Node inliningTarget) { + Object fun = self.getFunction(); + if (fun instanceof PFunction function) { + return function.getKwDefaultsDict(PythonLanguage.get(inliningTarget)); + } + assert fun instanceof PBuiltinFunction; + PKeyword[] kwdefaults = ((PBuiltinFunction) fun).getKwDefaults(); return (kwdefaults.length > 0) ? PFactory.createDict(PythonLanguage.get(inliningTarget), kwdefaults) : PNone.NONE; } } - - @Slot(SlotKind.tp_descr_get) - @GenerateUncached - @GenerateNodeFactory - public abstract static class GetNode extends DescrGetBuiltinNode { - @Specialization - @SuppressWarnings("unused") - static PMethod doGeneric(PMethod self, Object obj, Object cls) { - return self; - } - } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/PDecoratedMethod.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/PDecoratedMethod.java index c132d51694..acf047096e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/PDecoratedMethod.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/PDecoratedMethod.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 @@ -43,11 +43,9 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.BoundBuiltinCallable; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.objects.function.Signature; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.object.Shape; /** @@ -87,14 +85,4 @@ public Object boundToObject(PythonBuiltinClassType binding, PythonLanguage langu } return this; } - - public String getName() { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new UnsupportedOperationException(); - } - - public Signature getSignature() { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new UnsupportedOperationException(); - } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/MMapBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/MMapBuiltins.java index b05ca02ab9..c3c7a364cb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/MMapBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/MMapBuiltins.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 @@ -136,7 +136,6 @@ import com.oracle.graal.python.runtime.sequence.storage.ByteSequenceStorage; 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.ThreadLocalAction.Access; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.dsl.Bind; @@ -157,6 +156,7 @@ @CoreFunctions(extendClasses = PythonBuiltinClassType.PMMap) public final class MMapBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = MMapBuiltinsSlotsGen.SLOTS; + private static final TruffleString T_MMAP_NEW = tsLiteral("mmap.__new__"); @Override protected List> getNodeFactories() { @@ -227,7 +227,7 @@ static PMMap doFile(VirtualFrame frame, Object clazz, int fd, long lengthIn, int prot = PROT_READ.value | PROT_WRITE.value; break; case ACCESS_COPY: - flags = MAP_PRIVATE.value; + flags = MAP_PRIVATE.defined ? MAP_PRIVATE.getValueIfDefined() : 0; prot = PROT_READ.value | PROT_WRITE.value; break; case PMMap.ACCESS_DEFAULT: @@ -244,7 +244,7 @@ static PMMap doFile(VirtualFrame frame, Object clazz, int fd, long lengthIn, int throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.ValueError, ErrorMessages.MEM_MAPPED_OFFSET_INVALID_ACCESS); } - auditNode.audit(inliningTarget, "mmap.__new__", fd, lengthIn, access, offset); + auditNode.audit(frame, inliningTarget, T_MMAP_NEW, fd, lengthIn, access, offset); // For file mappings we use fstat to validate the length or to initialize the length if // it is 0 meaning that we should find it out for the user @@ -597,7 +597,7 @@ abstract static class ResizeNode extends PythonBuiltinNode { @SuppressWarnings("unused") static long resize(PMMap self, Object n, @Bind Node inliningTarget) { - // TODO: implement resize in NFI + // TODO: implement resize throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.SystemError, ErrorMessages.RESIZING_NOT_AVAILABLE); } } @@ -961,8 +961,9 @@ public void execute(PythonContext context, Access access) { return; } PythonLanguage language = context.getLanguage(); - CallTarget callTarget = language.createCachedCallTarget(MMapBuiltins.ReleaseCallback.ReleaserRootNode::new, MMapBuiltins.ReleaseCallback.ReleaserRootNode.class); - callTarget.call(ref); + ReleaserRootNode rootNode = language.createCachedRootNode(MMapBuiltins.ReleaseCallback.ReleaserRootNode::new, + MMapBuiltins.ReleaseCallback.ReleaserRootNode.class); + rootNode.getCallTarget().call(ref); } private static class ReleaserRootNode extends RootNode { 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..cec536ae08 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,7 +40,7 @@ */ package com.oracle.graal.python.builtins.objects.mmap; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary; import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAcquireLibrary; @@ -48,7 +48,7 @@ import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.runtime.AsyncHandler; -import com.oracle.graal.python.runtime.NFIPosixSupport; +import com.oracle.graal.python.runtime.NativePosixSupport; import com.oracle.graal.python.runtime.PosixSupportLibrary; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PythonContext; @@ -61,7 +61,6 @@ import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.object.Shape; -import com.oracle.truffle.api.strings.TruffleString; @ExportLibrary(PythonBufferAcquireLibrary.class) @ExportLibrary(PythonBufferAccessLibrary.class) @@ -140,13 +139,11 @@ int getBufferLength( byte readByte(int byteOffset, @Bind Node inliningTarget, @Shared @CachedLibrary(limit = "1") PosixSupportLibrary posixLib, - @Shared("raiseNode") @Cached PConstructAndRaiseNode.Lazy raiseNode, - @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode) { + @Shared("raiseNode") @Cached PConstructAndRaiseNode.Lazy raiseNode) { try { return posixLib.mmapReadByte(PythonContext.get(raiseNode).getPosixSupport(), getPosixSupportHandle(), byteOffset); } catch (PosixException e) { - // TODO(fa) how to handle? - throw raiseNode.get(inliningTarget).raiseOSError(null, e.getErrorCode(), fromJavaStringNode.execute(e.getMessage(), TS_ENCODING)); + throw raiseNode.get(inliningTarget).raiseOSErrorFromPosixException(null, e); } } @@ -154,13 +151,11 @@ byte readByte(int byteOffset, void writeByte(int byteOffset, byte value, @Bind Node inliningTarget, @Shared @CachedLibrary(limit = "1") PosixSupportLibrary posixLib, - @Shared("raiseNode") @Cached PConstructAndRaiseNode.Lazy raiseNode, - @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode) { + @Shared("raiseNode") @Cached PConstructAndRaiseNode.Lazy raiseNode) { try { posixLib.mmapWriteByte(PythonContext.get(raiseNode).getPosixSupport(), getPosixSupportHandle(), byteOffset, value); } catch (PosixException e) { - // TODO(fa) how to handle? - throw raiseNode.get(inliningTarget).raiseOSError(null, e.getErrorCode(), fromJavaStringNode.execute(e.getMessage(), TS_ENCODING)); + throw raiseNode.get(inliningTarget).raiseOSErrorFromPosixException(null, e); } } @@ -192,10 +187,9 @@ public AsyncHandler.AsyncAction release() { } void close(PosixSupportLibrary posixLib, Object posixSupport) { - if (isReleased()) { + if (!markReleased()) { return; } - markReleased(); Object handle = getReference(); if (fd != -1) { @@ -216,17 +210,17 @@ void close(PosixSupportLibrary posixLib, Object posixSupport) { @ExportMessage boolean isNative( @Bind Node inliningTarget) { - return PythonContext.get(inliningTarget).getPosixSupport() instanceof NFIPosixSupport; + return PythonContext.get(inliningTarget).getPosixSupport() instanceof NativePosixSupport; } @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/FrozenModules.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java index 8c7d926a52..e0aebd29fa 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java @@ -96,15 +96,12 @@ private static final class Map { private static final PythonFrozenModule __PHELLO___SPAM = new PythonFrozenModule("__PHELLO___SPAM", "__phello__.spam", false); private static final PythonFrozenModule FROZEN_ONLY = new PythonFrozenModule("FROZEN_ONLY", null, false); private static final PythonFrozenModule POLYGLOT_ARROW = new PythonFrozenModule("POLYGLOT_ARROW", null, false); - private static final PythonFrozenModule _SYSCONFIGDATA = new PythonFrozenModule("_SYSCONFIGDATA", null, false); private static final PythonFrozenModule _POLYGLOT = new PythonFrozenModule("_POLYGLOT", null, false); private static final PythonFrozenModule _POLYGLOT_TIME = new PythonFrozenModule("_POLYGLOT_TIME", null, false); private static final PythonFrozenModule GRAALPY___GRAALPYTHON__ = new PythonFrozenModule("GRAALPY___GRAALPYTHON__", null, false); private static final PythonFrozenModule GRAALPY__SRE = new PythonFrozenModule("GRAALPY__SRE", null, false); private static final PythonFrozenModule GRAALPY__SYSCONFIG = new PythonFrozenModule("GRAALPY__SYSCONFIG", null, false); private static final PythonFrozenModule GRAALPY_JAVA = new PythonFrozenModule("GRAALPY_JAVA", null, false); - private static final PythonFrozenModule GRAALPY_PIP_HOOK = new PythonFrozenModule("GRAALPY_PIP_HOOK", null, false); - private static final PythonFrozenModule GRAALPY__NT = new PythonFrozenModule("GRAALPY__NT", null, false); } public static final PythonFrozenModule lookup(String name) { @@ -223,8 +220,6 @@ public static final PythonFrozenModule lookup(String name) { return Map.FROZEN_ONLY; case "polyglot.arrow": return Map.POLYGLOT_ARROW; - case "_sysconfigdata": - return Map._SYSCONFIGDATA; case "_polyglot": return Map._POLYGLOT; case "_polyglot_time": @@ -237,10 +232,6 @@ public static final PythonFrozenModule lookup(String name) { return Map.GRAALPY__SYSCONFIG; case "graalpy.java": return Map.GRAALPY_JAVA; - case "graalpy.pip_hook": - return Map.GRAALPY_PIP_HOOK; - case "graalpy._nt": - return Map.GRAALPY__NT; default: return null; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java index f84fc13fd2..5a6129a3b4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.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 @@ -86,7 +86,6 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; @@ -238,7 +237,8 @@ static Object doError(Object self, @SuppressWarnings("unused") Object dict, @GenerateNodeFactory public abstract static class ModuleGetattributeNode extends GetAttrBuiltinNode { /** - * Keep in sync with {@link MergedObjectTypeModuleGetAttributeNode} + * Keep in sync with + * {@link com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode} */ @Specialization static Object getattribute(VirtualFrame frame, PythonModule self, Object keyObj, 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/namespace/SimpleNamespaceBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/namespace/SimpleNamespaceBuiltins.java index b9f21b53e9..b82c2ebd46 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/namespace/SimpleNamespaceBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/namespace/SimpleNamespaceBuiltins.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,7 +43,6 @@ import static com.oracle.graal.python.nodes.ErrorMessages.NO_POSITIONAL_ARGUMENTS_EXPECTED; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___DICT__; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__; -import static com.oracle.graal.python.nodes.SpecialMethodNames.T___REPR__; import static com.oracle.graal.python.nodes.StringLiterals.T_COMMA_SPACE; import static com.oracle.graal.python.nodes.StringLiterals.T_EQ; import static com.oracle.graal.python.nodes.StringLiterals.T_LPAREN; @@ -80,22 +79,19 @@ 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.slots.TpSlotRichCompare.RichCmpBuiltinNode; +import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; 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.PRaiseNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; -import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode; 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.exception.PythonErrorType; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Bind; @@ -204,7 +200,7 @@ abstract static class SimpleNamespaceReprNode extends PythonUnaryBuiltinNode { @CompilerDirectives.ValueType protected static final class NSReprState { private final HashingStorage dictStorage; - private final List> items; + private final ArrayList> items; @CompilerDirectives.TruffleBoundary NSReprState(HashingStorage dictStorage) { @@ -243,34 +239,25 @@ protected final int getLimit() { return limit; } - protected static TruffleString getReprString(Node inliningTarget, Object obj, - LookupAndCallUnaryNode.LookupAndCallUnaryDynamicNode reprNode, - CastToTruffleStringNode castStr, - PRaiseNode raiseNode) { - Object reprObj = reprNode.executeObject(obj, T___REPR__); - try { - return castStr.execute(inliningTarget, reprObj); - } catch (CannotCastException e) { - throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.RETURNED_NON_STRING, "__repr__", reprObj); - } + protected static TruffleString getReprString(Frame frame, Node inliningTarget, Object obj, + PyObjectReprAsTruffleStringNode reprNode) { + return reprNode.execute(frame, inliningTarget, obj); } @Override public abstract NSReprState execute(Frame frame, Node node, HashingStorage storage, HashingStorageIterator it, NSReprState state); @Specialization - public static NSReprState doPStringKey(@SuppressWarnings("unused") Node node, HashingStorage storage, HashingStorageIterator it, NSReprState state, + public static NSReprState doPStringKey(Frame frame, @SuppressWarnings("unused") Node node, HashingStorage storage, HashingStorageIterator it, NSReprState state, @Bind Node inliningTarget, - @Cached LookupAndCallUnaryNode.LookupAndCallUnaryDynamicNode valueReprNode, + @Cached PyObjectReprAsTruffleStringNode valueReprNode, @Cached CastToTruffleStringNode castStrKey, - @Cached CastToTruffleStringNode castStrValue, - @Cached PRaiseNode raiseNode, @Cached HashingStorageIteratorKey itKey, @Cached HashingStorageGetItem getItem) { Object keyObj = itKey.execute(inliningTarget, storage, it); if (PGuards.isString(keyObj)) { TruffleString key = castStrKey.execute(inliningTarget, keyObj); - TruffleString valueReprString = getReprString(inliningTarget, getItem.execute(inliningTarget, state.dictStorage, key), valueReprNode, castStrValue, raiseNode); + TruffleString valueReprString = getReprString(frame, inliningTarget, getItem.execute(inliningTarget, state.dictStorage, key), valueReprNode); appendItem(state, key, valueReprString); } return state; 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 3840a52b06..3c8f75e7c1 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 @@ -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. @@ -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; @@ -119,7 +123,6 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; -import com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes; @@ -145,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; @@ -347,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, - @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))); + static Object call(Node inliningTarget, Object cls, + @Cached CApiTransitions.PythonToNativeInternalNode toNativeNode, + @Cached CApiTransitions.NativeToPythonInternalNode toPythonNode) { + assert EnsurePythonObjectNode.doesNotNeedPromotion(cls); + long clsPointer = toNativeNode.execute(inliningTarget, cls); + try { + PythonContext context = PythonContext.get(inliningTarget); + var callable = CApiContext.getNativeSymbol(inliningTarget, FUN_PY_OBJECT_NEW); + long nativeResult = ExternalFunctionInvoker.invokePY_OBJECT_NEW(null, C_API_TIMING, + context.ensureNativeContext(), BoundaryCallData.getUncached(), + context.getThreadState(context.getLanguage(inliningTarget)), callable, clsPointer); + return toPythonNode.executeTransfer(inliningTarget, nativeResult); + } finally { + Reference.reachabilityFence(cls); + } } } @@ -510,7 +526,8 @@ public abstract static class GetAttributeNode extends GetAttrBuiltinNode { * Keep in sync with * {@link com.oracle.graal.python.builtins.objects.type.TypeBuiltins.GetattributeNode} and * {@link com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins.GetAttributeNode} - * and {@link MergedObjectTypeModuleGetAttributeNode} + * and + * {@link com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode} */ @Specialization @SuppressWarnings("truffle-static-method") 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..93207ee1d0 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,25 +72,26 @@ 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; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.PythonAbstractObject; 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.transitions.CApiTransitions.PythonToNativeNode; +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.common.EconomicMapStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageDelItem; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem; -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.dict.PDict; import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis; @@ -115,7 +116,9 @@ import com.oracle.graal.python.lib.PyImportImport; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetAttr; +import com.oracle.graal.python.lib.PyObjectGetItem; import com.oracle.graal.python.lib.PyObjectGetIter; +import com.oracle.graal.python.lib.PyObjectIsSubclassNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectLookupAttrO; import com.oracle.graal.python.lib.PyObjectSizeNode; @@ -141,8 +144,6 @@ import com.oracle.graal.python.runtime.PythonOptions; 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 +159,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 +315,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 +379,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); @@ -462,8 +454,7 @@ static Pair doNewArgsEx(VirtualFrame frame, Object getNewArgsExA @Exclusive @Cached CallNode callNode, @Exclusive @Cached PyTupleCheckNode tupleCheckNode, @Cached PyDictCheckNode isDictSubClassNode, - @Cached SequenceStorageNodes.GetItemNode getItemNode, - @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, + @Cached PyObjectGetItem getItemNode, @Cached PyObjectSizeNode sizeNode, @Exclusive @Cached PRaiseNode raiseNode) { Object newargs = callNode.execute(frame, getNewArgsExAttr); @@ -475,9 +466,8 @@ static Pair doNewArgsEx(VirtualFrame frame, Object getNewArgsExA throw raiseNode.raise(inliningTarget, ValueError, SHOULD_RETURN_A_NOT_B, T___GETNEWARGS_EX__, "tuple of length 2", length); } - SequenceStorage sequenceStorage = getSequenceStorageNode.execute(inliningTarget, newargs); - Object args = getItemNode.execute(sequenceStorage, 0); - Object kwargs = getItemNode.execute(sequenceStorage, 1); + Object args = getItemNode.execute(frame, inliningTarget, newargs, 0); + Object kwargs = getItemNode.execute(frame, inliningTarget, newargs, 1); if (!tupleCheckNode.execute(inliningTarget, args)) { throw raiseNode.raise(inliningTarget, TypeError, MUST_BE_TYPE_A_NOT_TYPE_B, "first item of the tuple returned by __getnewargs_ex__", "tuple", args); @@ -634,11 +624,19 @@ abstract static class CheckBasesizeForGetState extends Node { public abstract boolean execute(Node inliningTarget, Object obj, Object type, int slotNum); @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; + static boolean doNative(Node inliningTarget, @SuppressWarnings("unused") PythonAbstractNativeObject obj, Object type, int slotNum, + @Cached PythonToNativeInternalNode toNativeNode) { + assert EnsurePythonObjectNode.doesNotNeedPromotion(type); + long typePointer = toNativeNode.execute(inliningTarget, 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 @@ -673,10 +671,9 @@ static Object reduceNewObj(VirtualFrame frame, Node inliningTarget, Object obj, @Cached InlinedConditionProfile hasArgsProfile, @Cached(inline = false) GetNewArgsNode getNewArgsNode, @Cached ObjectGetStateNode getStateNode, - @Cached(inline = false) BuiltinFunctions.IsSubClassNode isSubClassNode, - @Cached SequenceNodes.GetSequenceStorageNode getSequenceStorageNode, - @Cached SequenceStorageNodes.ToArrayNode toArrayNode, + @Cached(inline = false) PyObjectIsSubclassNode isSubClassNode, @Cached PyObjectSizeNode sizeNode, + @Cached PyObjectGetItem getItemNode, @Exclusive @Cached PyObjectCallMethodObjArgs callMethod, @Cached PyObjectGetIter getIter, @Bind PythonLanguage language, @@ -700,11 +697,12 @@ static Object reduceNewObj(VirtualFrame frame, Node inliningTarget, Object obj, newobj = lookupAttr.execute(frame, inliningTarget, copyReg, T___NEWOBJ__); Object[] newargsVals; if (hasArgsProfile.profile(inliningTarget, hasargs)) { - SequenceStorage sequenceStorage = getSequenceStorageNode.execute(inliningTarget, args); - Object[] vals = toArrayNode.execute(inliningTarget, sequenceStorage); - newargsVals = new Object[vals.length + 1]; + int argsLen = sizeNode.execute(frame, inliningTarget, args); + newargsVals = new Object[argsLen + 1]; newargsVals[0] = cls; - System.arraycopy(vals, 0, newargsVals, 1, vals.length); + for (int i = 0; i < argsLen; i++) { + newargsVals[i + 1] = getItemNode.execute(frame, inliningTarget, args, i); + } } else { newargsVals = new Object[]{cls}; } @@ -716,8 +714,8 @@ static Object reduceNewObj(VirtualFrame frame, Node inliningTarget, Object obj, throw raiseNode.raiseBadInternalCall(inliningTarget); } - boolean objIsList = isSubClassNode.executeWith(frame, cls, PythonBuiltinClassType.PList); - boolean objIsDict = isSubClassNode.executeWith(frame, cls, PythonBuiltinClassType.PDict); + boolean objIsList = isSubClassNode.execute(frame, cls, PythonBuiltinClassType.PList); + boolean objIsDict = isSubClassNode.execute(frame, cls, PythonBuiltinClassType.PDict); boolean required = !hasargs && !objIsDict && !objIsList; Object state = getStateNode.execute(frame, inliningTarget, obj, required); 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 b61ca62fcd..4cf84d5c66 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,11 +33,16 @@ 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; import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -54,22 +59,41 @@ public class PythonObject extends PythonAbstractObject { public static final byte HAS_SLOTS_BUT_NO_DICT_FLAG = 0b1; /** * Indicates that the shape has some properties that may contain {@link PNone#NO_VALUE} and - * therefore the shape itself is not enough to resolve any lookups. + * therefore the shape itself is not enough to resolve any lookups. This flag is maintained only + * for types and not for all Python objects. */ public static final byte HAS_NO_VALUE_PROPERTIES = 0b10; /** - * Indicates that the object has a dict in the form of an actual dictionary + * Indicates that the object has the dict hidden key set. Use {@link #HAS_MATERIALIZED_DICT} to + * determine if the dict must be used. */ - public static final byte HAS_MATERIALIZED_DICT = 0b100; + public static final byte HAS_DICT = 0b100; + /** + * Indicates that the object has a dict in the form of an actual dictionary that is not backed + * by this object, i.e., is disconnected from the {@link DynamicObject} properties of this + * object. If this flag is set, all property accesses must operate on the dictionary object. + */ + public static final byte HAS_MATERIALIZED_DICT = 0b1000; /** * Indicates that the object is a static base in the CPython's tp_new_wrapper sense. * * @see com.oracle.graal.python.nodes.function.builtins.WrapTpNew */ - public static final byte IS_STATIC_BASE = 0b1000; + 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); @@ -82,6 +106,20 @@ public void setDict(Node inliningTarget, HiddenAttr.WriteNode writeNode, PDict d writeNode.execute(inliningTarget, this, HiddenAttr.DICT, dict); } + public boolean checkDictFlags() { + return checkDictFlags(GetDictIfExistsNode.getDictUncached(this)); + } + + public boolean checkDictFlags(PDict dict) { + assert dict == null || hasShapeFlag(HAS_DICT); + assert (dict == null || dict.getDictStorage() instanceof DynamicObjectStorage domStorage && domStorage.getStore() == this) || hasShapeFlag(HAS_MATERIALIZED_DICT); + return true; + } + + private boolean hasShapeFlag(int flag) { + return (GetShapeFlagsNode.getUncached().execute(this) & flag) != 0; + } + @NeverDefault public final Object getPythonClass() { return pythonClass; @@ -89,6 +127,7 @@ public final Object getPythonClass() { public final void setPythonClass(Object pythonClass) { assert getShape().getDynamicType() == PNone.NO_VALUE; + assert PGuards.isPythonClass(pythonClass); this.pythonClass = pythonClass; } @@ -144,4 +183,55 @@ 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; + } + + 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/posix/DirEntryBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/DirEntryBuiltins.java index b55ab28824..1560852b83 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/DirEntryBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/DirEntryBuiltins.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 @@ -58,9 +58,9 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; import com.oracle.graal.python.annotations.ArgumentClinic.ClinicConversion; +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.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; @@ -283,7 +283,7 @@ static PTuple uncachedLStatWithSymlink(VirtualFrame frame, PDirEntry self, boole long[] rawStat = posixLib.fstatat(context.getPosixSupport(), dirFd, posixPath.value, followSymlinks); res = PosixModuleBuiltins.createStatResult(inliningTarget, context.getLanguage(inliningTarget), positiveLongProfile, rawStat); } catch (PosixException e) { - if (catchNoent && e.getErrorCode() == OSErrorEnum.ENOENT.getNumber()) { + if (catchNoent && e.hasErrno(OSErrorEnum.ENOENT)) { return null; } throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e, posixPath.originalObject); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/PScandirIterator.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/PScandirIterator.java index 62b6450a5c..fbe75c77dc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/PScandirIterator.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/PScandirIterator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, 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 @@ -76,10 +76,9 @@ public AsyncAction release() { } void rewindAndClose(PosixSupportLibrary posixLib, Object posixSupport) { - if (isReleased()) { + if (!markReleased()) { return; } - markReleased(); Object dirStream = getReference(); if (needsRewind) { posixLib.rewinddir(posixSupport, dirStream); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/ScandirIteratorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/ScandirIteratorBuiltins.java index 275519195b..6e5b03fee1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/ScandirIteratorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/posix/ScandirIteratorBuiltins.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 @@ -65,7 +65,6 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.ThreadLocalAction.Access; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.dsl.Bind; @@ -170,8 +169,8 @@ public void execute(PythonContext context, Access access) { return; } PythonLanguage language = context.getLanguage(); - CallTarget callTarget = language.createCachedCallTarget(ReleaserRootNode::new, ReleaserRootNode.class); - callTarget.call(ref); + ReleaserRootNode rootNode = language.createCachedRootNode(ReleaserRootNode::new, ReleaserRootNode.class); + rootNode.getCallTarget().call(ref); } private static class ReleaserRootNode extends RootNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.java index 1f4d3e8446..33b12a6040 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/random/RandomBuiltins.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 @@ -47,7 +47,6 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.security.SecureRandom; import java.util.List; import com.oracle.graal.python.PythonLanguage; @@ -132,11 +131,10 @@ public abstract static class SeedNode extends PythonBuiltinNode { @Specialization @TruffleBoundary PNone seedNone(PRandom random, @SuppressWarnings("unused") PNone none) { - SecureRandom secureRandom = getContext().getSecureRandom(); int[] seed = new int[PRandom.N]; - for (int i = 0; i < seed.length; ++i) { - seed[i] = secureRandom.nextInt(); - } + byte[] seedBytes = new byte[seed.length * Integer.BYTES]; + getContext().fillInitializationEntropyBytes(seedBytes); + ByteBuffer.wrap(seedBytes).order(ByteOrder.BIG_ENDIAN).asIntBuffer().get(seed); random.seed(seed); return PNone.NONE; } 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/set/BaseSetBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/BaseSetBuiltins.java index 4542c92bde..b183fda862 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/BaseSetBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/BaseSetBuiltins.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 @@ -405,8 +405,7 @@ static Object doSet(VirtualFrame frame, PBaseSet self, PBaseSet other, @Bind Node inliningTarget, @Cached HashingStorageNodes.HashingStorageXor xorNode, @Cached CreateSetNode createSetNode) { - // TODO: calls __eq__ wrong number of times compared to CPython (GR-42240) - HashingStorage storage = xorNode.execute(frame, inliningTarget, self.getDictStorage(), other.getDictStorage()); + HashingStorage storage = xorNode.executePreservingLeft(frame, inliningTarget, other.getDictStorage(), self.getDictStorage()); return createSetNode.execute(inliningTarget, storage, self); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/FrozenSetBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/FrozenSetBuiltins.java index e06ca36f1d..d1b18929bf 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/FrozenSetBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/FrozenSetBuiltins.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. @@ -40,6 +40,7 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.common.EmptyStorage; import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes; +import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.GetSetStorageForXorNode; import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.GetSetStorageNode; import com.oracle.graal.python.builtins.objects.common.HashingStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes; @@ -52,6 +53,7 @@ import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorKey; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIteratorNext; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageXor; +import com.oracle.graal.python.builtins.objects.common.PHashingCollection; 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.slots.TpSlotHashFun.HashBuiltinNode; @@ -65,8 +67,10 @@ 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.Exclusive; import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateNodeFactory; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; @@ -195,15 +199,25 @@ static PFrozenSet doSet(@SuppressWarnings("unused") VirtualFrame frame, PFrozenS @Builtin(name = "symmetric_difference", minNumOfPositionalArgs = 2) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class SymmetricDifferenceNode extends PythonBuiltinNode { @Specialization - static PFrozenSet doSet(@SuppressWarnings("unused") VirtualFrame frame, PFrozenSet self, Object other, + static PFrozenSet doHashingCollection(VirtualFrame frame, PFrozenSet self, PHashingCollection other, + @Bind Node inliningTarget, + @Exclusive @Cached HashingStorageXor xorNode, + @Bind PythonLanguage language) { + HashingStorage result = xorNode.executePreservingLeft(frame, inliningTarget, other.getDictStorage(), self.getDictStorage()); + return PFactory.createFrozenSet(language, result); + } + + @Specialization(guards = "!isPHashingCollection(other)") + static PFrozenSet doGeneric(VirtualFrame frame, PFrozenSet self, Object other, @Bind Node inliningTarget, - @Cached HashingCollectionNodes.GetSetStorageNode getHashingStorage, - @Cached HashingStorageXor xorNode, + @Exclusive @Cached GetSetStorageForXorNode getHashingStorage, + @Exclusive @Cached HashingStorageXor xorNode, @Bind PythonLanguage language) { - HashingStorage result = xorNode.execute(frame, inliningTarget, self.getDictStorage(), getHashingStorage.execute(frame, inliningTarget, other)); + HashingStorage result = xorNode.executeMutatingLeft(frame, inliningTarget, getHashingStorage.execute(frame, inliningTarget, other), self.getDictStorage()); return PFactory.createFrozenSet(language, result); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetBuiltins.java index 6ff2efef46..0f1e5a2384 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetBuiltins.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. @@ -44,6 +44,7 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes; +import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.GetSetStorageForXorNode; import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.GetSetStorageNode; import com.oracle.graal.python.builtins.objects.common.HashingStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageAddAllToOther; @@ -500,7 +501,7 @@ public abstract static class IXorNode extends PythonBinaryBuiltinNode { static Object doSet(VirtualFrame frame, PSet self, PBaseSet other, @Bind Node inliningTarget, @Cached HashingStorageXor xorNode) { - self.setDictStorage(xorNode.execute(frame, inliningTarget, self.getDictStorage(), other.getDictStorage())); + self.setDictStorage(xorNode.executeMutatingLeft(frame, inliningTarget, self.getDictStorage(), other.getDictStorage())); return self; } @@ -513,15 +514,25 @@ Object doOr(Object self, Object other) { @Builtin(name = "symmetric_difference", minNumOfPositionalArgs = 2) @GenerateNodeFactory + @ImportStatic(PGuards.class) public abstract static class SymmetricDifferenceNode extends PythonBuiltinNode { @Specialization - static PSet doSet(VirtualFrame frame, PSet self, Object other, + static PSet doHashingCollection(VirtualFrame frame, PSet self, PHashingCollection other, + @Bind Node inliningTarget, + @Exclusive @Cached HashingStorageXor xorNode, + @Bind PythonLanguage language) { + HashingStorage result = xorNode.executePreservingLeft(frame, inliningTarget, other.getDictStorage(), self.getDictStorage()); + return PFactory.createSet(language, result); + } + + @Specialization(guards = "!isPHashingCollection(other)") + static PSet doGeneric(VirtualFrame frame, PSet self, Object other, @Bind Node inliningTarget, - @Cached GetSetStorageNode getHashingStorage, - @Cached HashingStorageXor xorNode, + @Exclusive @Cached GetSetStorageForXorNode getHashingStorage, + @Exclusive @Cached HashingStorageXor xorNode, @Bind PythonLanguage language) { - HashingStorage result = xorNode.execute(frame, inliningTarget, self.getDictStorage(), getHashingStorage.execute(frame, inliningTarget, other)); + HashingStorage result = xorNode.executeMutatingLeft(frame, inliningTarget, getHashingStorage.execute(frame, inliningTarget, other), self.getDictStorage()); return PFactory.createSet(language, result); } } @@ -540,11 +551,11 @@ static PNone doSet(VirtualFrame frame, PSet self, PNone other) { static PNone doCached(VirtualFrame frame, PSet self, Object[] args, @Bind Node inliningTarget, @Cached("args.length") int len, - @Shared @Cached HashingCollectionNodes.GetSetStorageNode getHashingStorage, + @Shared @Cached GetSetStorageForXorNode getHashingStorage, @Shared @Cached HashingStorageXor xorNode) { HashingStorage result = self.getDictStorage(); for (int i = 0; i < len; i++) { - result = xorNode.execute(frame, inliningTarget, result, getHashingStorage.execute(frame, inliningTarget, args[i])); + result = xorNode.executeMutatingLeft(frame, inliningTarget, result, getHashingStorage.execute(frame, inliningTarget, args[i])); } self.setDictStorage(result); return PNone.NONE; @@ -553,11 +564,11 @@ static PNone doCached(VirtualFrame frame, PSet self, Object[] args, @Specialization(replaces = "doCached") static PNone doSetArgs(VirtualFrame frame, PSet self, Object[] args, @Bind Node inliningTarget, - @Shared @Cached GetSetStorageNode getHashingStorage, + @Shared @Cached GetSetStorageForXorNode getHashingStorage, @Shared @Cached HashingStorageXor xorNode) { HashingStorage result = self.getDictStorage(); for (Object o : args) { - result = xorNode.execute(frame, inliningTarget, result, getHashingStorage.execute(frame, inliningTarget, o)); + result = xorNode.executeMutatingLeft(frame, inliningTarget, result, getHashingStorage.execute(frame, inliningTarget, o)); } self.setDictStorage(result); return PNone.NONE; @@ -570,9 +581,9 @@ static boolean isOther(Object arg) { @Specialization(guards = "isOther(other)") static PNone doSetOther(VirtualFrame frame, PSet self, Object other, @Bind Node inliningTarget, - @Shared @Cached HashingCollectionNodes.GetSetStorageNode getHashingStorage, + @Shared @Cached GetSetStorageForXorNode getHashingStorage, @Shared @Cached HashingStorageXor xorNode) { - HashingStorage result = xorNode.execute(frame, inliningTarget, self.getDictStorage(), getHashingStorage.execute(frame, inliningTarget, other)); + HashingStorage result = xorNode.executeMutatingLeft(frame, inliningTarget, self.getDictStorage(), getHashingStorage.execute(frame, inliningTarget, other)); self.setDictStorage(result); return PNone.NONE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetNodes.java index 31e991e996..70e4ff6f2a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/set/SetNodes.java @@ -117,7 +117,7 @@ public static ConstructSetNode getUncached() { } @GenerateUncached - @OperationProxy.Proxyable(storeBytecodeIndex = true) + @OperationProxy.Proxyable(storeBytecodeIndex = true, allowUncached = true) @GenerateInline(false) // footprint reduction 92 -> 73 public abstract static class AddNode extends PNodeWithContext { public abstract void execute(Frame frame, PSet self, Object o); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketBuiltins.java index d0fe8220c8..e80d199a7b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketBuiltins.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 @@ -55,6 +55,7 @@ import static com.oracle.graal.python.runtime.PosixConstants.SO_ERROR; import static com.oracle.graal.python.runtime.PosixConstants.SO_PROTOCOL; import static com.oracle.graal.python.runtime.PosixConstants.SO_TYPE; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.util.List; @@ -97,6 +98,7 @@ import com.oracle.graal.python.runtime.PosixConstants; import com.oracle.graal.python.runtime.PosixSupport; import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult; import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; @@ -125,6 +127,10 @@ public final class SocketBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = SocketBuiltinsSlotsGen.SLOTS; + private static final TruffleString T_SOCKET_NEW = tsLiteral("socket.__new__"); + private static final TruffleString T_SOCKET_BIND = tsLiteral("socket.bind"); + private static final TruffleString T_SOCKET_CONNECT = tsLiteral("socket.connect"); + private static final TruffleString T_SOCKET_SENDTO = tsLiteral("socket.sendto"); @Override protected List> getNodeFactories() { @@ -172,7 +178,7 @@ static Object init(VirtualFrame frame, PSocket self, int familyIn, int typeIn, i @Cached GilNode gil, @Exclusive @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { // sic! CPython really has __new__ there, even though it's in __init__ - auditNode.audit(inliningTarget, "socket.__new__", self, familyIn, typeIn, protoIn); + auditNode.audit(frame, inliningTarget, T_SOCKET_NEW, self, familyIn, typeIn, protoIn); int family = familyIn; if (family == -1) { family = PosixConstants.AF_INET.value; @@ -224,7 +230,7 @@ static Object init(VirtualFrame frame, PSocket self, int familyIn, int typeIn, i @Exclusive @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Cached PRaiseNode raiseNode) { // sic! CPython really has __new__ there, even though it's in __init__ - auditNode.audit(inliningTarget, "socket.__new__", self, familyIn, typeIn, protoIn); + auditNode.audit(frame, inliningTarget, T_SOCKET_NEW, self, familyIn, typeIn, protoIn); int fd = asIntNode.execute(frame, inliningTarget, fileno); if (fd < 0) { @@ -237,7 +243,7 @@ static Object init(VirtualFrame frame, PSocket self, int familyIn, int typeIn, i family = addrLib.getFamily(addr); } } catch (PosixException e) { - if (family == -1 || e.getErrorCode() == EBADF.getNumber() || e.getErrorCode() == ENOTSOCK.getNumber()) { + if (family == -1 || e.hasErrno(EBADF) || e.hasErrno(ENOTSOCK)) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } @@ -352,7 +358,7 @@ static Object bind(VirtualFrame frame, PSocket self, Object address, @Cached GilNode gil, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { UniversalSockAddr addr = getSockAddrArgNode.execute(frame, self, address, "bind"); - auditNode.audit(inliningTarget, "socket.bind", self, address); + auditNode.audit(frame, inliningTarget, T_SOCKET_BIND, self, address); try { gil.release(true); @@ -391,7 +397,7 @@ static Object close(VirtualFrame frame, PSocket socket, } } catch (PosixException e) { // CPython ignores ECONNRESET on close - if (e.getErrorCode() != OSErrorEnum.ECONNRESET.getNumber()) { + if (!e.hasErrno(OSErrorEnum.ECONNRESET)) { throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } @@ -415,7 +421,7 @@ static Object connect(VirtualFrame frame, PSocket self, Object address, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { UniversalSockAddr connectAddr = getSockAddrArgNode.execute(frame, self, address, "connect"); - auditNode.audit(inliningTarget, "socket.connect", self, address); + auditNode.audit(frame, inliningTarget, T_SOCKET_CONNECT, self, address); try { doConnect(frame, inliningTarget, constructAndRaiseNode, posixLib, context.getPosixSupport(), gil, self, connectAddr); @@ -436,11 +442,11 @@ static void doConnect(Frame frame, Node inliningTarget, PConstructAndRaiseNode.L } } catch (PosixException e) { boolean waitConnect; - if (e.getErrorCode() == EINTR.getNumber()) { + if (e.hasErrno(EINTR)) { PythonContext.triggerAsyncActions(constructAndRaiseNode); waitConnect = self.getTimeoutNs() != 0 && isSelectable(self); } else { - waitConnect = self.getTimeoutNs() > 0 && e.getErrorCode() == EINPROGRESS.getNumber() && isSelectable(self); + waitConnect = self.getTimeoutNs() > 0 && e.hasErrno(EINPROGRESS) && isSelectable(self); } if (waitConnect) { SocketUtils.callSocketFunctionWithRetry(frame, inliningTarget, constructAndRaiseNode, posixLib, posixSupport, gil, self, @@ -449,7 +455,7 @@ static void doConnect(Frame frame, Node inliningTarget, PConstructAndRaiseNode.L p.getsockopt(s, self.getFd(), SOL_SOCKET.value, SO_ERROR.value, tmp, tmp.length); int err = PythonUtils.ARRAY_ACCESSOR.getInt(tmp, 0); if (err != 0 && err != EISCONN.getNumber()) { - throw new PosixException(err, p.strerror(s, err)); + throw new PosixErrnoException(err, p.strerror(s, err)); } return null; }, @@ -476,12 +482,15 @@ static Object connectEx(VirtualFrame frame, PSocket self, Object address, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { UniversalSockAddr connectAddr = getSockAddrArgNode.execute(frame, self, address, "connect_ex"); - auditNode.audit(inliningTarget, "socket.connect", self, address); // sic! connect + auditNode.audit(frame, inliningTarget, T_SOCKET_CONNECT, self, address); // sic! connect try { ConnectNode.doConnect(frame, inliningTarget, constructAndRaiseNode, posixLib, context.getPosixSupport(), gil, self, connectAddr); } catch (PosixException e) { - return e.getErrorCode(); + if (e instanceof PosixErrnoException errnoException) { + return errnoException.getErrorCode(); + } + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } return 0; } @@ -972,7 +981,7 @@ static Object sendTo(VirtualFrame frame, PSocket socket, Object bufferObj, Objec checkSelectable(inliningTarget, raiseNode, socket); UniversalSockAddr addr = getSockAddrArgNode.execute(frame, socket, address, "sendto"); - auditNode.audit(inliningTarget, "socket.sendto", socket, address); + auditNode.audit(frame, inliningTarget, T_SOCKET_SENDTO, socket, address); int len = bufferLib.getBufferLength(buffer); byte[] bytes = bufferLib.getInternalOrCopiedByteArray(buffer); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketNodes.java index 09ba8816d8..58440770ce 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketNodes.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 @@ -343,6 +343,8 @@ static UniversalSockAddr setipaddr(VirtualFrame frame, byte[] name, int family, } } catch (GetAddrInfoException e) { throw constructAndRaiseNode.get(inliningTarget).executeWithArgsOnly(frame, SocketGAIError, new Object[]{e.getErrorCode(), e.getMessage()}); + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } @@ -395,7 +397,6 @@ static Object makeSockAddr(VirtualFrame frame, Node inliningTarget, UniversalSoc @CachedLibrary(limit = "1") PosixSupportLibrary posixLib, @CachedLibrary("addr") UniversalSockAddrLibrary addrLib, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, - @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, @Cached PRaiseNode raiseNode) { @@ -428,7 +429,7 @@ static Object makeSockAddr(VirtualFrame frame, Node inliningTarget, UniversalSoc throw raiseNode.raise(inliningTarget, NotImplementedError, toTruffleStringUncached("makesockaddr: unknown address family")); } } catch (PosixException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSError(frame, e.getErrorCode(), fromJavaStringNode.execute(e.getMessage(), TS_ENCODING)); + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } @@ -456,7 +457,6 @@ static Object makeAddr(VirtualFrame frame, Node inliningTarget, UniversalSockAdd @CachedLibrary(limit = "1") PosixSupportLibrary posixLib, @CachedLibrary("addr") UniversalSockAddrLibrary addrLib, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, - @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @Cached PRaiseNode raiseNode) { try { PythonContext context = PythonContext.get(inliningTarget); @@ -473,7 +473,7 @@ static Object makeAddr(VirtualFrame frame, Node inliningTarget, UniversalSockAdd throw raiseNode.raise(inliningTarget, NotImplementedError, toTruffleStringUncached("makesockaddr: unknown address family")); } } catch (PosixException e) { - throw constructAndRaiseNode.get(inliningTarget).raiseOSError(frame, e.getErrorCode(), fromJavaStringNode.execute(e.getMessage(), TS_ENCODING)); + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketUtils.java index 5db99d3457..399bffaf39 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/socket/SocketUtils.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 @@ -49,6 +49,7 @@ import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; import com.oracle.graal.python.runtime.PythonContext; @@ -107,8 +108,8 @@ public static T callSocketFunctionWithRetry(Frame frame, Node inliningTarget } finally { gil.acquire(); } - } catch (PosixException e) { - if (e.getErrorCode() == EINTR.getNumber()) { + } catch (PosixErrnoException e) { + if (e.hasErrno(EINTR)) { PythonContext.triggerAsyncActions(constructAndRaiseNode); continue; } @@ -124,12 +125,12 @@ public static T callSocketFunctionWithRetry(Frame frame, Node inliningTarget } finally { gil.acquire(); } - } catch (PosixException e) { - if (e.getErrorCode() == EINTR.getNumber()) { + } catch (PosixErrnoException e) { + if (e.hasErrno(EINTR)) { PythonContext.triggerAsyncActions(constructAndRaiseNode); continue; } - if (timeoutHelper != null && (e.getErrorCode() == EWOULDBLOCK.getNumber() || e.getErrorCode() == EAGAIN.getNumber())) { + if (timeoutHelper != null && (e.hasErrno(EWOULDBLOCK) || e.hasErrno(EAGAIN))) { /* * False positive: sock_func() failed with EWOULDBLOCK or EAGAIN. For * example, select() could indicate a socket is ready for reading, but the diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/CertUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/CertUtils.java index 7cffee1acc..7ece06023f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/CertUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/CertUtils.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 @@ -61,12 +61,15 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigInteger; +import java.security.GeneralSecurityException; import java.security.InvalidKeyException; +import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.Provider; +import java.security.Security; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; @@ -76,29 +79,26 @@ import java.security.cert.CertificateFactory; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.RSAPrivateCrtKeySpec; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.cert.X509CRLHolder; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.openssl.PEMEncryptedKeyPair; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; -import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder; -import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; -import org.bouncycastle.pkcs.PKCSException; -import org.bouncycastle.util.encoders.DecoderException; +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.objects.common.HashingStorage; @@ -108,6 +108,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.crypto.BouncyCastleSupportProvider; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleFile; @@ -142,6 +143,23 @@ public NeedsPasswordException() { } } + public static final class BadBase64Exception extends IOException { + private static final long serialVersionUID = -8554489203372180057L; + + public BadBase64Exception(Throwable cause) { + super(cause); + } + } + + private record PemBlock(String type, byte[] content) { + } + + private record KeyPemBlock(String type, byte[] content, Map headers) { + } + + private record PemBlockWithContent(String type, String content, int nextIndex) { + } + /** * openssl v3_purp.c#check_ca */ @@ -315,6 +333,7 @@ private static PTuple parseSubjectAltName(X509Certificate certificate, PythonLan // private static private static final class DerValue { + private static final byte INTEGER = 0x02; private static final byte OCTET_STRING = 0x04; private static final byte OBJECT_IDENTIFIER = 0x06; private static final byte SEQUENCE = 0x10; @@ -380,6 +399,31 @@ byte[] getRawData() { return Arrays.copyOfRange(data, contentStart, contentStart + contentLen); } + BigInteger getInteger() throws CertificateParsingException { + if (contentTag != INTEGER) { + throw new CertificateParsingException(ERROR_MESSAGE); + } + return new BigInteger(getRawData()); + } + + List getSequenceElements() throws CertificateParsingException { + if (contentTag != SEQUENCE) { + throw new CertificateParsingException(ERROR_MESSAGE); + } + ArrayList values = new ArrayList<>(); + int offset = contentStart; + int end = contentStart + contentLen; + while (offset < end) { + DerValue value = new DerValue(data, offset, end); + values.add(value); + offset = value.contentStart + value.contentLen; + } + if (offset != end) { + throw new CertificateParsingException(ERROR_MESSAGE); + } + return values; + } + DerValue getObjectIdentifier() throws CertificateParsingException { if (contentTag != OBJECT_IDENTIFIER) { return null; @@ -431,14 +475,6 @@ String getGeneralNameURI() { } } - List getSequenceElements() throws CertificateParsingException { - List result = new ArrayList<>(); - iterateSequence((e, r) -> { - result.add(e); - }, result); - return result; - } - @FunctionalInterface private interface DerSequenceConsumer { abstract void accept(A a, B b) throws CertificateParsingException; @@ -609,61 +645,124 @@ public static List getCertificates(BufferedReader r) throws IOException, @TruffleBoundary public static List getCertificates(BufferedReader r, boolean onlyCertificates) throws IOException, CertificateException, CRLException { List l = new ArrayList<>(); - PEMParser pemParser = new PEMParser(r); CertificateFactory factory = CertificateFactory.getInstance("X.509"); - Object object; - while ((object = pemParser.readObject()) != null) { - if (object instanceof X509CertificateHolder) { - // TODO use the X509CertificateHolder directly without conversion - l.add(factory.generateCertificate(new ByteArrayInputStream(((X509CertificateHolder) object).getEncoded()))); - } - if (!onlyCertificates && object instanceof X509CRLHolder) { - // TODO use the X509CRLHolder directly without conversion - l.add(factory.generateCRL(new ByteArrayInputStream(((X509CRLHolder) object).getEncoded()))); + for (PemBlock block : readPemBlocks(r)) { + if ("CERTIFICATE".equals(block.type())) { + l.add(factory.generateCertificate(new ByteArrayInputStream(block.content()))); + } else if (!onlyCertificates && ("X509 CRL".equals(block.type()) || "CRL".equals(block.type()))) { + l.add(factory.generateCRL(new ByteArrayInputStream(block.content()))); } } return l; } + private static List readPemBlocks(BufferedReader reader) throws IOException { + StringBuilder text = new StringBuilder(); + char[] buffer = new char[8192]; + int read; + while ((read = reader.read(buffer)) != -1) { + text.append(buffer, 0, read); + } + + String data = text.toString(); + List blocks = new ArrayList<>(); + int fromIndex = 0; + while (true) { + int begin = data.indexOf("-----BEGIN ", fromIndex); + if (begin < 0) { + return blocks; + } + int typeStart = begin + "-----BEGIN ".length(); + int typeEnd = data.indexOf("-----", typeStart); + if (typeEnd < 0) { + throw new IOException("Malformed PEM header"); + } + String type = data.substring(typeStart, typeEnd); + int contentStart = typeEnd + "-----".length(); + if (contentStart < data.length() && data.charAt(contentStart) == '\r') { + contentStart++; + } + if (contentStart < data.length() && data.charAt(contentStart) == '\n') { + contentStart++; + } + + String endMarker = "-----END " + type + "-----"; + int end = data.indexOf(endMarker, contentStart); + if (end < 0) { + throw new IOException("Missing PEM footer"); + } + + String content = data.substring(contentStart, end); + if (isCertificatePemType(type)) { + blocks.add(new PemBlock(type, decodePemContent(content))); + } + fromIndex = end + endMarker.length(); + } + } + + private static boolean isCertificatePemType(String type) { + return "CERTIFICATE".equals(type) || "X509 CRL".equals(type) || "CRL".equals(type); + } + + private static byte[] decodePemContent(String content) throws IOException { + StringBuilder base64 = new StringBuilder(content.length()); + for (String line : content.split("\\R")) { + String trimmed = line.trim(); + if (trimmed.isEmpty()) { + continue; + } + if (trimmed.indexOf(':') >= 0) { + throw new IOException("Unexpected PEM headers"); + } + base64.append(trimmed); + } + if (base64.length() == 0) { + throw new IOException("Empty PEM content"); + } + try { + return Base64.getMimeDecoder().decode(base64.toString()); + } catch (IllegalArgumentException e) { + throw new BadBase64Exception(e); + } + } + // No BoundaryCallContext: constructs and raises only builtin errors @TruffleBoundary static PrivateKey getPrivateKey(PythonContext context, Node inliningTarget, PConstructAndRaiseNode.Lazy raiseNode, BufferedReader reader, char[] password, X509Certificate cert) throws NeedsPasswordException { - PEMParser pemParser = new PEMParser(reader); - JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); - Provider provider = LazyBouncyCastleProvider.initProvider(); - converter.setProvider(provider); PrivateKey privateKey = null; + String algorithm = cert.getPublicKey().getAlgorithm(); try { - Object object; - while ((object = pemParser.readObject()) != null) { - PrivateKeyInfo pkInfo; - if (object instanceof PEMKeyPair) { - pkInfo = ((PEMKeyPair) object).getPrivateKeyInfo(); - } else if (object instanceof PEMEncryptedKeyPair) { + String pemText = readAll(reader); + int fromIndex = 0; + PemBlockWithContent rawBlock; + while ((rawBlock = findNextPemBlock(pemText, fromIndex)) != null) { + fromIndex = rawBlock.nextIndex(); + KeyPemBlock block = decodePrivateKeyPemBlock(rawBlock.type(), rawBlock.content()); + if ("PRIVATE KEY".equals(block.type())) { + privateKey = decodePrivateKey(algorithm, block.content()); + break; + } else if ("ENCRYPTED PRIVATE KEY".equals(block.type())) { if (password == null) { throw new NeedsPasswordException(); } - JcePEMDecryptorProviderBuilder decryptor = new JcePEMDecryptorProviderBuilder(); - decryptor.setProvider(provider); - PEMKeyPair keyPair = ((PEMEncryptedKeyPair) object).decryptKeyPair(decryptor.build(password)); - pkInfo = keyPair.getPrivateKeyInfo(); - } else if (object instanceof PKCS8EncryptedPrivateKeyInfo) { - if (password == null) { - throw new NeedsPasswordException(); + privateKey = decodeEncryptedPrivateKey(algorithm, block.content(), password); + break; + } else if (isLegacyPrivateKeyType(block.type())) { + if ("RSA PRIVATE KEY".equals(block.type()) && block.headers().isEmpty()) { + privateKey = decodeRsaPrivateKey(block.content()); + } else { + if (!block.headers().isEmpty() && password == null) { + throw new NeedsPasswordException(); + } + privateKey = BouncyCastleSupportProvider.loadPrivateKey(password, pemBlockToText(rawBlock)); } - JceOpenSSLPKCS8DecryptorProviderBuilder decryptor = new JceOpenSSLPKCS8DecryptorProviderBuilder(); - decryptor.setProvider(provider); - pkInfo = ((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decryptor.build(password)); - } else if (object instanceof PrivateKeyInfo) { - pkInfo = (PrivateKeyInfo) object; - } else { - continue; + break; } - privateKey = converter.getPrivateKey(pkInfo); - break; } - } catch (IOException | DecoderException | OperatorCreationException | PKCSException e) { + } catch (BouncyCastleSupportProvider.MissingBouncyCastleException e) { + throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_SSL, toTruffleStringUncached(e.getMessage())); + } catch (IOException | GeneralSecurityException e) { throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_SSL_PEM_LIB, ErrorMessages.SSL_PEM_LIB); } if (privateKey == null) { @@ -674,36 +773,214 @@ static PrivateKey getPrivateKey(PythonContext context, Node inliningTarget, PCon return privateKey; } + private static String readAll(BufferedReader reader) throws IOException { + StringBuilder sb = new StringBuilder(); + char[] buf = new char[8192]; + int n; + while ((n = reader.read(buf)) != -1) { + sb.append(buf, 0, n); + } + return sb.toString(); + } + + private static PemBlockWithContent findNextPemBlock(String data, int fromIndex) throws IOException { + int begin = data.indexOf("-----BEGIN ", fromIndex); + if (begin < 0) { + return null; + } + int typeStart = begin + "-----BEGIN ".length(); + int typeEnd = data.indexOf("-----", typeStart); + if (typeEnd < 0) { + throw new IOException("Malformed PEM header"); + } + String type = data.substring(typeStart, typeEnd); + int contentStart = typeEnd + "-----".length(); + if (contentStart < data.length() && data.charAt(contentStart) == '\r') { + contentStart++; + } + if (contentStart < data.length() && data.charAt(contentStart) == '\n') { + contentStart++; + } + String endMarker = "-----END " + type + "-----"; + int end = data.indexOf(endMarker, contentStart); + if (end < 0) { + throw new IOException("Missing PEM footer"); + } + return new PemBlockWithContent(type, data.substring(contentStart, end), end + endMarker.length()); + } + + private static KeyPemBlock decodePrivateKeyPemBlock(String type, String content) throws IOException { + Map headers = new LinkedHashMap<>(); + StringBuilder base64 = new StringBuilder(content.length()); + boolean seenBase64 = false; + for (String line : content.split("\\R")) { + String trimmed = line.trim(); + if (trimmed.isEmpty()) { + continue; + } + int colon = trimmed.indexOf(':'); + if (!seenBase64 && colon >= 0) { + headers.put(trimmed.substring(0, colon).trim(), trimmed.substring(colon + 1).trim()); + } else { + if (colon >= 0) { + throw new IOException("Malformed PEM content"); + } + seenBase64 = true; + base64.append(trimmed); + } + } + if (base64.length() == 0) { + throw new IOException("Empty PEM content"); + } + try { + return new KeyPemBlock(type, Base64.getMimeDecoder().decode(base64.toString()), headers); + } catch (IllegalArgumentException e) { + throw new BadBase64Exception(e); + } + } + + private static boolean isLegacyPrivateKeyType(String type) { + return "RSA PRIVATE KEY".equals(type) || "DSA PRIVATE KEY".equals(type) || "EC PRIVATE KEY".equals(type); + } + + private static String pemBlockToText(PemBlockWithContent block) { + return "-----BEGIN " + block.type() + "-----\n" + block.content() + "\n-----END " + block.type() + "-----\n"; + } + + private static PrivateKey decodePrivateKey(String algorithm, byte[] encoded) throws NoSuchAlgorithmException, InvalidKeySpecException { + return getKeyFactory(algorithm).generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } + + private static PrivateKey decodeEncryptedPrivateKey(String algorithm, byte[] encoded, char[] password) throws IOException, GeneralSecurityException { + EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encoded); + SecretKeyFactory secretKeyFactory = getSecretKeyFactory(encryptedPrivateKeyInfo.getAlgName()); + SecretKey secretKey = secretKeyFactory.generateSecret(new PBEKeySpec(password)); + Provider provider = getPreferredSecurityProvider(); + PKCS8EncodedKeySpec keySpec = provider != null ? encryptedPrivateKeyInfo.getKeySpec(secretKey, provider) : encryptedPrivateKeyInfo.getKeySpec(secretKey); + return getKeyFactory(algorithm).generatePrivate(keySpec); + } + + private static PrivateKey decodeRsaPrivateKey(byte[] encoded) throws InvalidKeySpecException, NoSuchAlgorithmException { + try { + DerValue sequence = new DerValue(encoded); + List values = sequence.getSequenceElements(); + if (values.size() < 9) { + throw new InvalidKeySpecException("Invalid RSA private key"); + } + BigInteger version = values.get(0).getInteger(); + if (!BigInteger.ZERO.equals(version) && !BigInteger.ONE.equals(version)) { + throw new InvalidKeySpecException("Unsupported RSA private key version"); + } + RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec( + values.get(1).getInteger(), + values.get(2).getInteger(), + values.get(3).getInteger(), + values.get(4).getInteger(), + values.get(5).getInteger(), + values.get(6).getInteger(), + values.get(7).getInteger(), + values.get(8).getInteger()); + return getKeyFactory("RSA").generatePrivate(keySpec); + } catch (CertificateParsingException e) { + throw new InvalidKeySpecException("Invalid RSA private key", e); + } + } + + private static KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException { + Provider provider = getPreferredSecurityProvider(); + return provider != null ? KeyFactory.getInstance(algorithm, provider) : KeyFactory.getInstance(algorithm); + } + + private static SecretKeyFactory getSecretKeyFactory(String algorithm) throws NoSuchAlgorithmException { + Provider provider = getPreferredSecurityProvider(); + return provider != null ? SecretKeyFactory.getInstance(algorithm, provider) : SecretKeyFactory.getInstance(algorithm); + } + + private static Provider getPreferredSecurityProvider() { + String providerName = System.getProperty("python.security.provider"); + if (providerName == null || providerName.isEmpty()) { + return null; + } + return Security.getProvider(providerName); + } + private static void checkPrivateKey(Node inliningTarget, PConstructAndRaiseNode.Lazy raiseNode, PythonContext context, PrivateKey privateKey, PublicKey publicKey) { /* * Check that the private key matches the public key by signing and verifying a short piece * of data. */ + byte[] data = new byte[128]; + context.getSecureRandom().nextBytes(data); try { - Signature sign; + String signatureAlgorithm = privateKey.getAlgorithm(); + if ("EC".equals(signatureAlgorithm)) { + signatureAlgorithm = "ECDSA"; + } try { - sign = Signature.getInstance(String.format("SHA256with%s", privateKey.getAlgorithm())); + if (checkPrivateKey("SHA256with%s".formatted(signatureAlgorithm), privateKey, publicKey, data)) { + return; + } } catch (NoSuchAlgorithmException e) { - sign = Signature.getInstance(String.format("SHA1with%s", privateKey.getAlgorithm())); - } - sign.initSign(privateKey); - byte[] data = new byte[128]; - context.getSecureRandom().nextBytes(data); - sign.update(data); - byte[] signature = sign.sign(); - sign.initVerify(publicKey); - sign.update(data); - if (sign.verify(signature)) { - return; + if (checkPrivateKey("SHA1with%s".formatted(signatureAlgorithm), privateKey, publicKey, data)) { + return; + } } } catch (NoSuchAlgorithmException e) { throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_SSL, e); - } catch (SignatureException | InvalidKeyException e) { + } catch (SignatureException e) { // fallthrough } throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_KEY_VALUES_MISMATCH, ErrorMessages.KEY_VALUES_MISMATCH); } + private static boolean checkPrivateKey(String signatureAlgorithm, PrivateKey privateKey, PublicKey publicKey, byte[] data) throws NoSuchAlgorithmException, SignatureException { + Provider preferredProvider = getPreferredSecurityProvider(); + if (preferredProvider != null && checkPrivateKey(signatureAlgorithm, preferredProvider, privateKey, publicKey, data)) { + return true; + } + Provider[] providers = Security.getProviders("Signature." + signatureAlgorithm); + if (providers != null) { + for (Provider provider : providers) { + if ((preferredProvider == null || !preferredProvider.getName().equals(provider.getName())) && checkPrivateKey(signatureAlgorithm, provider, privateKey, publicKey, data)) { + return true; + } + } + } + if (checkPrivateKeyWithBouncyCastle(signatureAlgorithm, privateKey, publicKey, data)) { + return true; + } + return checkPrivateKey(signatureAlgorithm, (Provider) null, privateKey, publicKey, data); + } + + private static boolean checkPrivateKey(String signatureAlgorithm, Provider provider, PrivateKey privateKey, PublicKey publicKey, byte[] data) throws NoSuchAlgorithmException, SignatureException { + try { + Signature signature = provider != null ? Signature.getInstance(signatureAlgorithm, provider) : Signature.getInstance(signatureAlgorithm); + signature.initSign(privateKey); + signature.update(data); + byte[] signed = signature.sign(); + signature.initVerify(publicKey); + signature.update(data); + boolean verified = signature.verify(signed); + return verified; + } catch (InvalidKeyException e) { + return false; + } + } + + private static boolean checkPrivateKeyWithBouncyCastle(String signatureAlgorithm, PrivateKey privateKey, PublicKey publicKey, byte[] data) throws NoSuchAlgorithmException, SignatureException { + try { + Signature signature = BouncyCastleSupportProvider.createSignature(signatureAlgorithm); + signature.initSign(privateKey); + signature.update(data); + byte[] signed = signature.sign(); + signature.initVerify(publicKey); + signature.update(data); + return signature.verify(signed); + } catch (InvalidKeyException e) { + return false; + } + } + @TruffleBoundary static Collection generateCertificates(byte[] bytes) throws CertificateException { // test_load_verify_cadata appends an extra byte to a valid certificate and expects diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLCipherSelector.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLCipherSelector.java index 341a460e0f..506c496a2a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLCipherSelector.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLCipherSelector.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 @@ -118,7 +118,7 @@ private static void selectSingle(Node node, String cipherString, List if (cipherString.startsWith("@STRENGTH")) { selected.sort(Comparator.comparingInt(SSLCipher::getStrengthBits).reversed()); } else if (cipherString.startsWith("@SECLEVEL=")) { - throw PRaiseNode.raiseStatic(node, NotImplementedError, toTruffleStringUncached("@SECLEVEL not implemented")); + handleSecurityLevel(node, cipherString); } else { EncapsulatingNodeReference nodeRef = EncapsulatingNodeReference.getCurrent(); Node prev = nodeRef.set(node); @@ -140,6 +140,34 @@ private static void selectSingle(Node node, String cipherString, List } } + private static void handleSecurityLevel(Node node, String cipherString) { + String levelString = cipherString.substring("@SECLEVEL=".length()); + if ("1".equals(levelString)) { + return; + } + if (!isDecimalDigits(levelString)) { + throw PRaiseNode.raiseStatic(node, + NotImplementedError, + toTruffleStringUncached("Unsupported OpenSSL cipher string directive: " + cipherString)); + } + throw PRaiseNode.raiseStatic(node, + NotImplementedError, + toTruffleStringUncached("Unsupported OpenSSL security level @SECLEVEL=" + levelString + "; only @SECLEVEL=1 is supported")); + } + + private static boolean isDecimalDigits(String value) { + if (value.isEmpty()) { + return false; + } + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); + if (ch < '0' || ch > '9') { + return false; + } + } + return true; + } + private static List getCiphersForCipherString(Node node, String cipherString) { List result = null; for (String component : cipherString.split("\\+")) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java index 21df03c257..2746186462 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.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 @@ -76,7 +76,7 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; -import org.bouncycastle.util.encoders.DecoderException; +import com.oracle.graal.python.builtins.objects.ssl.CertUtils.BadBase64Exception; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; @@ -173,7 +173,7 @@ static PSSLContext createContext(VirtualFrame frame, Object type, int protocol, @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, @Cached PRaiseNode raiseNode) { SSLMethod method = SSLMethod.fromPythonId(protocol); - if (method == null) { + if (method == null || isUnsupportedSingleVersion(method)) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.INVALID_OR_UNSUPPORTED_PROTOCOL_VERSION, "NULL"); } try { @@ -190,6 +190,9 @@ static PSSLContext createContext(VirtualFrame frame, Object type, int protocol, createSSLContext()); long options = SSLOptions.SSL_OP_ALL; if (method != SSLMethod.SSL3) { + // supportedProtocols describes runtime availability. This per-context option still keeps + // generic TLS contexts from negotiating SSLv3. Only an explicit, supported SSLv3 + // context may leave it enabled. options |= SSLOptions.SSL_OP_NO_SSLv3; } context.setOptions(options); @@ -201,6 +204,19 @@ static PSSLContext createContext(VirtualFrame frame, Object type, int protocol, } } + @TruffleBoundary + private static boolean isUnsupportedSingleVersion(SSLMethod method) { + if (!method.isSingleVersion()) { + return false; + } + for (SSLProtocol supportedProtocol : SSLModuleBuiltins.getSupportedProtocols()) { + if (method.allowsProtocol(supportedProtocol)) { + return false; + } + } + return true; + } + @TruffleBoundary private static SSLContext createSSLContext() throws NoSuchAlgorithmException, KeyManagementException { SSLContext context = SSLContext.getInstance("TLS"); @@ -637,7 +653,7 @@ private void setBoundary(PSSLContext self, PyUnicodeFSDecoderNode asPath) { LOGGER.fine(() -> String.format("set_default_verify_paths file: %s. path: %s", file != null ? file.getPath() : "None", path != null ? path.getPath() : "None")); try { self.setCAEntries(CertUtils.loadVerifyLocations(file, path)); - } catch (IOException | DecoderException | GeneralSecurityException | NoCertificateFoundException ex) { + } catch (IOException | GeneralSecurityException | NoCertificateFoundException ex) { // do not raise any errors LOGGER.log(Level.FINER, "", ex); } @@ -756,7 +772,7 @@ Object load(VirtualFrame frame, PSSLContext self, Object cafile, Object capath, self.setCAEntries(CertUtils.loadVerifyLocations(file, path)); } catch (NoCertificateFoundException e) { throw constructAndRaiseNode.get(inliningTarget).raiseSSLError(frame, SSLErrorCode.ERROR_NO_CERTIFICATE_OR_CRL_FOUND, ErrorMessages.NO_CERTIFICATE_OR_CRL_FOUND); - } catch (IOException | DecoderException e) { + } catch (IOException | CertificateException | CRLException e) { throw constructAndRaiseNode.get(inliningTarget).raiseSSLError(frame, SSLErrorCode.ERROR_SSL_PEM_LIB, ErrorMessages.X509_PEM_LIB); } } @@ -792,9 +808,9 @@ private static List getCertificates(Node inliningTarget, PConstructAndRa throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_NO_START_LINE, ErrorMessages.SSL_PEM_NO_START_LINE); } return certificates; - } catch (DecoderException e) { + } catch (BadBase64Exception e) { throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_BAD_BASE64_DECODE, ErrorMessages.BAD_BASE64_DECODE); - } catch (IOException e) { + } catch (IOException | CertificateException | CRLException e) { throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_SSL_PEM_LIB, ErrorMessages.SSL_PEM_LIB); } } @@ -889,7 +905,7 @@ private static Object load(PythonContext context, Node inliningTarget, PConstruc if (certs.length == 0) { throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_SSL_PEM_LIB, ErrorMessages.SSL_PEM_LIB); } - } catch (IOException | DecoderException e) { + } catch (IOException | CertificateException | CRLException e) { throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_SSL_PEM_LIB, ErrorMessages.SSL_PEM_LIB); } // if keyReader and certReader are from the same file, key is expected to come first diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLOperationNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLOperationNode.java index 6a0b34fbf8..82bd4f07c2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLOperationNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLOperationNode.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,7 +43,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.MemoryError; import static com.oracle.graal.python.builtins.objects.exception.OSErrorEnum.EAGAIN; import static com.oracle.graal.python.builtins.objects.exception.OSErrorEnum.EWOULDBLOCK; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import java.nio.ByteBuffer; @@ -76,7 +75,6 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.strings.TruffleString; /** * This class implements equivalents of OpenSSL transport functions ({@code SSL_read} etc) on top of @@ -176,7 +174,6 @@ static void doSocket(VirtualFrame frame, Node inliningTarget, PSSLSocket socket, @CachedLibrary(limit = "1") PosixSupportLibrary posixLib, @Cached(inline = false) GilNode gil, @Shared @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode, - @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @Shared @Cached PRaiseNode raiseNode) { assert socket.getSocket() != null; prepare(socket); @@ -237,13 +234,13 @@ static void doSocket(VirtualFrame frame, Node inliningTarget, PSSLSocket socket, } networkInboundBIO.advanceWritePosition(recvlen); } catch (PosixException e) { - if (e.getErrorCode() == EAGAIN.getNumber() || e.getErrorCode() == EWOULDBLOCK.getNumber()) { + if (e.hasErrno(EAGAIN) || e.hasErrno(EWOULDBLOCK)) { throw constructAndRaiseNode.get(inliningTarget).raiseSSLError(frame, SSLErrorCode.ERROR_WANT_READ, ErrorMessages.SSL_WANT_READ); } if (socket.hasSavedException()) { throw socket.getAndClearSavedException(); } - throw constructAndRaiseNode.get(inliningTarget).raiseOSError(frame, e.getErrorCode(), fromJavaStringNode.execute(e.getMessage(), TS_ENCODING)); + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } break; case WANTS_WRITE: @@ -257,13 +254,13 @@ static void doSocket(VirtualFrame frame, Node inliningTarget, PSSLSocket socket, true, false, timeoutHelper); networkOutboundBIO.advanceReadPosition(writtenBytes); } catch (PosixException e) { - if (e.getErrorCode() == EAGAIN.getNumber() || e.getErrorCode() == EWOULDBLOCK.getNumber()) { + if (e.hasErrno(EAGAIN) || e.hasErrno(EWOULDBLOCK)) { throw constructAndRaiseNode.get(inliningTarget).raiseSSLError(frame, SSLErrorCode.ERROR_WANT_WRITE, ErrorMessages.SSL_WANT_WRITE); } if (socket.hasSavedException()) { throw socket.getAndClearSavedException(); } - throw constructAndRaiseNode.get(inliningTarget).raiseOSError(frame, e.getErrorCode(), fromJavaStringNode.execute(e.getMessage(), TS_ENCODING)); + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); } break; } @@ -554,6 +551,10 @@ private static PException handleSSLException(Node inliningTarget, SSLException e if (e.getCause() instanceof CertificateException) { throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_CERT_VERIFICATION, ErrorMessages.CERTIFICATE_VERIFY_FAILED, e.toString()); } - throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_SSL, toTruffleStringUncached(e.toString())); + String message = e.toString(); + if (message.contains("Unrecognized SSL message, plaintext connection?")) { + message = "before TLS handshake with data"; + } + throw raiseNode.get(inliningTarget).raiseSSLError(null, SSLErrorCode.ERROR_SSL, toTruffleStringUncached(message)); } } 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 deleted file mode 100644 index abfd18d625..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/NativeStringData.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 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.str; - -import com.oracle.graal.python.runtime.sequence.storage.NativeByteSequenceStorage; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.strings.TruffleString; - -public final class NativeStringData { - private static final byte KIND_ASCII = 0; - private static final byte KIND_1BYTE = 1; - private static final byte KIND_2BYTE = 2; - private static final byte KIND_4BYTE = 4; - private final byte kind; - // We need the storage object for memory management, don't inline its fields here - private final NativeByteSequenceStorage storage; - - private NativeStringData(int charSize, boolean isAscii, NativeByteSequenceStorage storage) { - assert charSize == 1 || charSize == 2 || charSize == 4; - assert !isAscii || charSize == 1; - this.kind = isAscii ? KIND_ASCII : (byte) charSize; - this.storage = storage; - } - - public static NativeStringData create(int charSize, boolean isAscii, Object ptr, int length) { - return new NativeStringData(charSize, isAscii, NativeByteSequenceStorage.create(ptr, length, length, true)); - } - - public boolean isAscii() { - return kind == KIND_ASCII; - } - - public int getCharSize() { - return kind != 0 ? kind : KIND_1BYTE; - } - - public Object getPtr() { - return storage.getPtr(); - } - - public int length() { - return storage.length(); - } - - public TruffleString toTruffleString(TruffleString.FromNativePointerNode fromNativePointerNode) { - TruffleString.Encoding encoding = switch (kind) { - case KIND_ASCII -> TruffleString.Encoding.US_ASCII; - case KIND_1BYTE -> TruffleString.Encoding.ISO_8859_1; - case KIND_2BYTE -> TruffleString.Encoding.UTF_16; - case KIND_4BYTE -> TruffleString.Encoding.UTF_32; - default -> throw CompilerDirectives.shouldNotReachHere(); - }; - // NativeByteSequenceStorage implements asPointer - return fromNativePointerNode.execute(storage, 0, storage.length(), encoding, false); - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/PString.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/PString.java index ba1c90c833..88bccb681c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/PString.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/PString.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. @@ -66,14 +66,6 @@ public TruffleString getValueUncached() { return isMaterialized() ? getMaterialized() : StringMaterializeNode.executeUncached(this); } - public NativeStringData getNativeStringData(Node inliningTarget, HiddenAttr.ReadNode readNode) { - return (NativeStringData) readNode.execute(inliningTarget, this, HiddenAttr.PSTRING_NATIVE_DATA, null); - } - - public void setNativeStringData(Node inliningTarget, HiddenAttr.WriteNode writeNode, NativeStringData value) { - writeNode.execute(inliningTarget, this, HiddenAttr.PSTRING_NATIVE_DATA, value); - } - public PBytes getUtf8Bytes(Node inliningTarget, HiddenAttr.ReadNode readNode) { return (PBytes) readNode.execute(inliningTarget, this, HiddenAttr.PSTRING_UTF8, null); } @@ -107,9 +99,7 @@ public void setMaterialized(TruffleString materialized) { @TruffleBoundary public void intern() { assert isBuiltinPString(this); - TruffleString ts = getValueUncached(); - TruffleString interned = PythonUtils.internString(ts); - materializedValue = interned; + materializedValue = PythonUtils.internString(getValueUncached()); } @Override 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 6b7afc2514..fe5b1e1df2 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 @@ -74,7 +74,6 @@ import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions; import com.oracle.graal.python.builtins.modules.CodecsModuleBuiltins; import com.oracle.graal.python.builtins.modules.TRegexUtil; import com.oracle.graal.python.builtins.objects.PNone; @@ -107,6 +106,7 @@ import com.oracle.graal.python.builtins.objects.slice.SliceNodes.CoerceToIntSlice; import com.oracle.graal.python.builtins.objects.slice.SliceNodes.ComputeIndices; import com.oracle.graal.python.builtins.objects.str.StringBuiltinsClinicProviders.FormatNodeClinicProviderGen; +import com.oracle.graal.python.builtins.objects.str.StringBuiltinsClinicProviders.SplitLinesNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.str.StringBuiltinsClinicProviders.SplitNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToJavaStringCheckedNode; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; @@ -133,6 +133,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqContains.SqContainsBuiltinNode; import com.oracle.graal.python.lib.PyIndexCheckNode; import com.oracle.graal.python.lib.PyNumberAsSizeNode; +import com.oracle.graal.python.lib.PyObjectFormat; import com.oracle.graal.python.lib.PyObjectGetItem; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; @@ -157,7 +158,6 @@ import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.nodes.util.CannotCastException; -import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; @@ -171,7 +171,6 @@ 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.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -320,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)); } } @@ -336,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 { @@ -357,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 { @@ -460,7 +459,7 @@ abstract static class StrFormatNode extends PythonBuiltinNode { static TruffleString format(VirtualFrame frame, Object self, Object[] args, PKeyword[] kwargs, @Bind Node inliningTarget, @Cached("createFor($node)") BoundaryCallData boundaryCallData, - @Cached BuiltinFunctions.FormatNode format, + @Cached PyObjectFormat format, @Cached CastToTruffleStringNode castToStringNode, @Cached PRaiseNode raiseNode) { TruffleString string; @@ -494,7 +493,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization TruffleString format(VirtualFrame frame, TruffleString self, Object mapping, @Cached("createFor($node)") BoundaryCallData boundaryCallData, - @Cached BuiltinFunctions.FormatNode format) { + @Cached PyObjectFormat format) { TemplateFormatter template = new TemplateFormatter(self); @@ -892,7 +891,7 @@ static TruffleString lowerAscii(TruffleString self, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, @Cached TruffleString.ByteIndexOfCodePointSetNode indexOfCodePointSetNode, @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { TruffleString ascii = switchEncodingNode.execute(self, Encoding.US_ASCII); int i = indexOfCodePointSetNode.execute(ascii, 0, ascii.byteLength(Encoding.US_ASCII), ASCII_UPPER); if (i < 0) { @@ -905,7 +904,7 @@ static TruffleString lowerAscii(TruffleString self, buf[i] = (byte) (buf[i] - 'A' + 'a'); } } - return switchEncodingNode.execute(fromByteArrayNode.execute(buf, Encoding.US_ASCII, false), TS_ENCODING); + return fromByteArrayNode.execute(buf, 0, buf.length, TruffleString.CompactionLevel.S1, false); } @Specialization(guards = "!isAscii(self, getCodeRangeNode)") @@ -939,7 +938,7 @@ static TruffleString upperAscii(TruffleString self, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, @Cached TruffleString.ByteIndexOfCodePointSetNode indexOfCodePointSetNode, @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { TruffleString ascii = switchEncodingNode.execute(self, Encoding.US_ASCII); int i = indexOfCodePointSetNode.execute(ascii, 0, ascii.byteLength(Encoding.US_ASCII), ASCII_LOWER); if (i < 0) { @@ -952,7 +951,7 @@ static TruffleString upperAscii(TruffleString self, buf[i] = (byte) (buf[i] - 'a' + 'A'); } } - return switchEncodingNode.execute(fromByteArrayNode.execute(buf, Encoding.US_ASCII, false), TS_ENCODING); + return fromByteArrayNode.execute(buf, 0, buf.length, TruffleString.CompactionLevel.S1, false); } @Specialization(guards = "!isAscii(self, getCodeRangeNode)") @@ -1132,43 +1131,36 @@ static TruffleString doGeneric(VirtualFrame frame, Object self, Object table, @GenerateNodeFactory public abstract static class CapitalizeNode extends PythonUnaryBuiltinNode { - @CompilationFinal private static CaseMap.Title titlecaser; - @Specialization static TruffleString capitalize(TruffleString self, + @Bind PythonLanguage language, @Cached TruffleString.ToJavaStringNode toJavaStringNode, @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode) { if (self.isEmpty()) { return T_EMPTY_STRING; } else { - return fromJavaStringNode.execute(capitalizeImpl(toJavaStringNode.execute(self)), TS_ENCODING); + return fromJavaStringNode.execute(capitalizeImpl(language.getCachedICUTitleCaser(), toJavaStringNode.execute(self)), TS_ENCODING); } } @Specialization static TruffleString doGeneric(Object self, @Bind Node inliningTarget, + @Bind PythonLanguage language, @Cached CastToJavaStringCheckedNode castToJavaStringNode, @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode) { String s = castToJavaStringNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "capitalize", self); if (s.isEmpty()) { return T_EMPTY_STRING; } - return fromJavaStringNode.execute(capitalizeImpl(s), TS_ENCODING); - } - - private static String capitalizeImpl(String str) { - if (titlecaser == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - titlecaser = CaseMap.toTitle().wholeString().noBreakAdjustment(); - } - return apply(str); + return fromJavaStringNode.execute(capitalizeImpl(language.getCachedICUTitleCaser(), s), TS_ENCODING); } @TruffleBoundary - private static String apply(String str) { - return titlecaser.apply(Locale.ROOT, null, str); + private static String capitalizeImpl(CaseMap.Title titleCaser, String str) { + return titleCaser.apply(Locale.ROOT, null, str); } + } // str.partition @@ -1277,7 +1269,7 @@ static PList doStringSep(TruffleString self, TruffleString sep, int maxsplit, if (sep.isEmpty()) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.EMPTY_SEPARATOR); } - int splits = maxsplit == -1 ? Integer.MAX_VALUE : maxsplit; + int splits = maxsplit < 0 ? Integer.MAX_VALUE : maxsplit; PList list = PFactory.createList(PythonLanguage.get(inliningTarget)); int lastEnd = 0; @@ -1460,14 +1452,13 @@ static PList doStringMaxsplit(VirtualFrame frame, TruffleString s, @SuppressWarn // str.splitlines([keepends]) @Builtin(name = "splitlines", minNumOfPositionalArgs = 1, parameterNames = {"self", "keepends"}) + @ArgumentClinic(name = "keepends", conversion = ClinicConversion.Boolean, defaultValue = "false") @GenerateNodeFactory - public abstract static class SplitLinesNode extends PythonBinaryBuiltinNode { + public abstract static class SplitLinesNode extends PythonBinaryClinicBuiltinNode { - @Specialization - static PList doString(TruffleString self, @SuppressWarnings("unused") PNone keepends, - @Bind Node inliningTarget, - @Cached @Shared SplitLinesInnerNode innerNode) { - return innerNode.execute(inliningTarget, self, false); + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return SplitLinesNodeClinicProviderGen.INSTANCE; } @Specialization @@ -1477,15 +1468,13 @@ static PList doStringKeepends(TruffleString selfTs, boolean keepends, return innerNode.execute(inliningTarget, selfTs, keepends); } - @Specialization(replaces = {"doString", "doStringKeepends"}) - static PList doGeneric(Object self, Object keepends, + @Specialization(replaces = "doStringKeepends") + static PList doGeneric(Object self, boolean keepends, @Bind Node inliningTarget, @Cached CastToTruffleStringChecked2Node castSelfNode, - @Cached CastToJavaIntExactNode castToJavaIntNode, @Cached @Exclusive SplitLinesInnerNode innerNode) { TruffleString selfStr = castSelfNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "splitlines", self); - boolean bKeepends = !PGuards.isPNone(keepends) && castToJavaIntNode.execute(inliningTarget, keepends) != 0; - return innerNode.execute(inliningTarget, selfStr, bKeepends); + return innerNode.execute(inliningTarget, selfStr, keepends); } @GenerateCached(false) @@ -2238,8 +2227,8 @@ static TruffleString doGeneric(VirtualFrame frame, Object selfObj, Object widthO abstract static class TitleNode extends PythonUnaryClinicBuiltinNode { @Specialization - @TruffleBoundary static TruffleString doString(TruffleString self, + @Bind PythonLanguage language, @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, @Cached TruffleStringIterator.NextNode nextNode, @Cached TruffleStringBuilder.AppendStringNode appendStringNode, @@ -2254,27 +2243,36 @@ static TruffleString doString(TruffleString self, int end = 0; while (it.hasNext()) { final int cp = nextNode.execute(it, TS_ENCODING); - if (!UCharacter.isLowerCase(cp) && !UCharacter.isUpperCase(cp)) { + if (notUpperOrLowerCase(cp)) { if (start == end) { appendCodePointNode.execute(sb, cp, 1, true); } else { - appendSegment(self, appendStringNode, substringNode, toJavaStringNode, fromJavaStringNode, sb, start, end); + appendSegment(self, language, appendStringNode, substringNode, toJavaStringNode, fromJavaStringNode, sb, start, end); } start = end + 1; } end++; } if (start != end) { - appendSegment(self, appendStringNode, substringNode, toJavaStringNode, fromJavaStringNode, sb, start, end - 1); + appendSegment(self, language, appendStringNode, substringNode, toJavaStringNode, fromJavaStringNode, sb, start, end - 1); } return toStringNode.execute(sb); } - private static void appendSegment(TruffleString self, TruffleStringBuilder.AppendStringNode appendStringNode, TruffleString.SubstringNode substringNode, + private static void appendSegment(TruffleString self, PythonLanguage language, TruffleStringBuilder.AppendStringNode appendStringNode, TruffleString.SubstringNode substringNode, TruffleString.ToJavaStringNode toJavaStringNode, TruffleString.FromJavaStringNode fromJavaStringNode, TruffleStringBuilderUTF32 sb, int start, int end) { TruffleString segment = substringNode.execute(self, start, end - start + 1, TS_ENCODING, true); - String titleSegment = UCharacter.toTitleCase(Locale.ROOT, toJavaStringNode.execute(segment), null); - appendStringNode.execute(sb, fromJavaStringNode.execute(titleSegment, TS_ENCODING)); + appendStringNode.execute(sb, fromJavaStringNode.execute(applyTitleCase(language.getCachedICUTitleCaser(), toJavaStringNode.execute(segment)), TS_ENCODING)); + } + + @TruffleBoundary + private static boolean notUpperOrLowerCase(int cp) { + return !UCharacter.isULowercase(cp) && !UCharacter.isUUppercase(cp); + } + + @TruffleBoundary + private static String applyTitleCase(CaseMap.Title titleCaser, String s) { + return titleCaser.apply(Locale.ROOT, null, s); } @Override 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 1f7217c5bd..7da4308958 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 @@ -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 @@ -53,9 +53,16 @@ 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.CApiTransitions.PythonToNativeNode; +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.PythonToNativeInternalNode; +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; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.ints.PInt; @@ -66,7 +73,6 @@ import com.oracle.graal.python.lib.PyIterNextNode; import com.oracle.graal.python.lib.PyObjectGetIter; 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.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; @@ -76,13 +82,16 @@ 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; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.graal.python.util.OverflowException; import com.oracle.graal.python.util.PythonUtils; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -107,27 +116,47 @@ public abstract class StringNodes { @GenerateUncached @GenerateInline @GenerateCached(false) - @ImportStatic(StringNodes.class) public abstract static class StringMaterializeNode extends Node { public static TruffleString executeUncached(PString s) { - return StringMaterializeNodeGen.getUncached().execute(null, s); + CompilerAsserts.neverPartOfCompilation(); + return StringMaterializeNodeGen.getUncached().execute(null, s, false); + } + + public final TruffleString execute(Node inliningTarget, PString materialize) { + return execute(inliningTarget, materialize, false); } - public abstract TruffleString execute(Node inliningTarget, PString materialize); + public abstract TruffleString execute(Node inliningTarget, PString materialize, boolean copyNativeData); @Specialization(guards = "x.isMaterialized()") - static TruffleString doMaterialized(PString x) { + static TruffleString doMaterialized(PString x, @SuppressWarnings("unused") boolean copyNativeData) { return x.getMaterialized(); } - @Fallback + @Specialization(guards = "!x.isMaterialized()") @InliningCutoff - static TruffleString doNative(Node inliningTarget, PString x, - @Cached HiddenAttr.ReadNode readAttrNode, - @Cached TruffleString.FromNativePointerNode fromNativePointerNode) { - NativeStringData nativeData = x.getNativeStringData(inliningTarget, readAttrNode); - TruffleString materialized = nativeData.toTruffleString(fromNativePointerNode); + static TruffleString doNative(PString x, boolean copyNativeData, + @Cached TruffleString.FromNativePointerWithCompactionUTF32Node fromNativePointerNode) { + assert x.isNative(); + assert PythonToNativeInternalNode.executeUncached(x, false) == x.getNativePointer(); + long ptr = HandlePointerConverter.pointerToStub(x.getNativePointer()); + long data = CStructAccess.readPtrField(ptr, CFields.GraalPyUnicodeObject__data); + assert data != 0; + int byteLength; + try { + byteLength = PInt.intValueExact(CStructAccess.readLongField(ptr, CFields.GraalPyUnicodeObject__byte_length)); + } catch (OverflowException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + int kind = CApiTransitions.getGraalPyUnicodeObjectKind(ptr); + TruffleString.CompactionLevel compactionLevel = switch (kind) { + case 1 -> TruffleString.CompactionLevel.S1; + case 2 -> TruffleString.CompactionLevel.S2; + case 4 -> TruffleString.CompactionLevel.S4; + default -> throw CompilerDirectives.shouldNotReachHere(); + }; + TruffleString materialized = fromNativePointerNode.execute(data, 0, byteLength, compactionLevel, copyNativeData); x.setMaterialized(materialized); return materialized; } @@ -137,6 +166,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); @@ -146,24 +176,20 @@ static int doString(TruffleString str, return codePointLengthNode.execute(str, TS_ENCODING); } - @Specialization(guards = "x.isMaterialized()") - static int doMaterialized(PString x, - @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode) { - return doString(x.getMaterialized(), codePointLengthNode); - } - - @Specialization(guards = "!x.isMaterialized()") - static int doNative(PString x, + @Specialization + static int doPString(PString x, @Bind Node inliningTarget, - @Cached HiddenAttr.ReadNode readAttrNode, - @Cached InlinedConditionProfile oneByteProfile, - @Cached StringMaterializeNode materializeNode, + @Cached InlinedConditionProfile isMaterializedProfile, @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); + if (isMaterializedProfile.profile(inliningTarget, x.isMaterialized())) { + return doString(x.getMaterialized(), codePointLengthNode); + } + assert x.isNative(); + long ptr = HandlePointerConverter.pointerToStub(x.getNativePointer()); + try { + return PInt.intValueExact(CStructAccess.readLongField(ptr, CFields.GraalPyUnicodeObject__length)); + } catch (OverflowException e) { + throw CompilerDirectives.shouldNotReachHere(e); } } @@ -173,14 +199,22 @@ static int doNativeObject(PythonNativeObject x, @Bind Node inliningTarget, @Cached GetClassNode getClassNode, @Cached IsSubtypeNode isSubtypeNode, - @Cached PCallCapiFunction callNativeUnicodeAsStringNode, - @Cached PythonToNativeNode toSulongNode, + @Cached PythonToNativeInternalNode 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); + long lresult = ExternalFunctionInvoker.invokePY_UNICODE_GET_LENGTH(null, C_API_TIMING, context.ensureNativeContext(), + BoundaryCallData.getUncached(), + context.getThreadState(context.getLanguage(inliningTarget)), callable, + toNativeNode.execute(inliningTarget, x)); + try { + return PInt.intValueExact(lresult); + } catch (OverflowException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } } // the object's type is not a subclass of 'str' throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.BAD_ARG_TYPE_FOR_BUILTIN_OP); @@ -195,11 +229,6 @@ static int other(Object x, TruffleString tstring = cast.cast(inliningTarget, x, ErrorMessages.DESCRIPTOR_REQUIRES_S_OBJ_RECEIVED_P, "str", x); return doString(tstring, codePointLengthNode); } - - @TruffleBoundary - private static int intValue(Number result) { - return result.intValue(); - } } @GenerateUncached diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java index ddb1db140c..298da48ed4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.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 @@ -48,17 +48,14 @@ import java.util.List; import java.util.Locale; -import org.graalvm.nativeimage.ImageInfo; import org.graalvm.shadowed.com.ibm.icu.lang.UCharacter; import org.graalvm.shadowed.com.ibm.icu.lang.UCharacterCategory; import org.graalvm.shadowed.com.ibm.icu.lang.UProperty; 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.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; -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; @@ -223,17 +220,6 @@ public static TruffleString strip(TruffleString str, TruffleString chars, StripK @TruffleBoundary public static boolean isPrintable(int codepoint) { - if (ImageInfo.inImageBuildtimeCode()) { - // Executing ICU4J at image build time causes issues with runtime/build time - // initialization - assert codepoint < 0x100; - return codepoint >= 32; - } - return isPrintableICU(codepoint); - } - - @TruffleBoundary - private static boolean isPrintableICU(int codepoint) { // ICU's definition of printability is different from CPython, so we cannot use // UCharacter.isPrintable int category = UCharacter.getType(codepoint); @@ -254,32 +240,16 @@ private static boolean isPrintableICU(int codepoint) { @TruffleBoundary public static String toLowerCase(String self) { - if (ImageInfo.inImageBuildtimeCode()) { - // Avoid initializing ICU4J in image build - return self.toLowerCase(); - } return UCharacter.toLowerCase(Locale.ROOT, self); } @TruffleBoundary public static String toUpperCase(String str) { - if (ImageInfo.inImageBuildtimeCode()) { - // Avoid initializing ICU4J in image build - return str.toUpperCase(); - } return UCharacter.toUpperCase(Locale.ROOT, str); } @TruffleBoundary public static boolean isAlnum(int codePoint) { - if (ImageInfo.inImageBuildtimeCode()) { - // Avoid initializing ICU4J in image build - return Character.isLetterOrDigit(codePoint); - } - return isAlnumICU(codePoint); - } - - private static boolean isAlnumICU(int codePoint) { if (UCharacter.isLetter(codePoint) || UCharacter.isDigit(codePoint) || UCharacter.hasBinaryProperty(codePoint, UProperty.NUMERIC_TYPE)) { return true; } @@ -337,22 +307,12 @@ static boolean doString(TruffleString str, @TruffleBoundary static boolean isIdentifierStart(int codePoint) { - if (ImageInfo.inImageBuildtimeCode()) { - // Avoid initializing ICU4J at image build time - return Character.isUnicodeIdentifierStart(codePoint); - } else { - return UCharacter.hasBinaryProperty(codePoint, UProperty.XID_START); - } + return UCharacter.hasBinaryProperty(codePoint, UProperty.XID_START); } @TruffleBoundary static boolean isIdentifierPart(int codePoint) { - if (ImageInfo.inImageBuildtimeCode()) { - // Avoid initializing ICU4J at image build time - return Character.isUnicodeIdentifierPart(codePoint); - } else { - return UCharacter.hasBinaryProperty(codePoint, UProperty.XID_CONTINUE); - } + return UCharacter.hasBinaryProperty(codePoint, UProperty.XID_CONTINUE); } } @@ -586,30 +546,6 @@ public static int getCodePoint(String characterName) { } } - /** - * Like {@link com.oracle.truffle.api.strings.TruffleString.EqualNode} but with the proper - * {@link InliningCutoff} since {@link com.oracle.truffle.api.strings.TruffleString.EqualNode} - * is too big for host inlining, at least when used in node guards. - */ - @GenerateInline - @GenerateCached(false) - @GenerateUncached - public abstract static class EqualNode extends Node { - public abstract boolean execute(Node inliningTarget, TruffleString left, TruffleString right); - - @Specialization(guards = "left == right") - static boolean doIdentity(TruffleString left, TruffleString right) { - return true; - } - - @InliningCutoff - @Fallback - static boolean doEquality(TruffleString left, TruffleString right, - @Cached TruffleString.EqualNode equalNode) { - return equalNode.execute(left, right, TS_ENCODING); - } - } - public static int codepointIndexToByteIndex(int codepointIndex) { assert TS_ENCODING == TruffleString.Encoding.UTF_32 : "must be adapted when switching to a different encoding"; return codepointIndex << 2; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/TemplateFormatter.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/TemplateFormatter.java index 644561c894..04cc82eebc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/TemplateFormatter.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/TemplateFormatter.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 @@ -63,17 +63,16 @@ import java.math.BigInteger; import java.util.ArrayList; -import java.util.List; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions.FormatNode; import com.oracle.graal.python.builtins.modules.SysModuleBuiltins; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.function.PKeyword; -import com.oracle.graal.python.lib.PyObjectAsciiNode; +import com.oracle.graal.python.lib.PyObjectAsciiAsObjectNode; +import com.oracle.graal.python.lib.PyObjectFormat; import com.oracle.graal.python.lib.PyObjectGetItem; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; -import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; +import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.nodes.Node; @@ -91,7 +90,7 @@ public final class TemplateFormatter { private String empty; private Object[] args; private Object keywords; - private List parserList = null; + private ArrayList parserList = null; private int autoNumbering; private int autoNumberingState; @@ -103,7 +102,7 @@ public TemplateFormatter(TruffleString template) { } @TruffleBoundary - public TruffleString build(Node node, Object[] argsArg, Object kwArgs, FormatNode formatNode) { + public TruffleString build(Node node, Object[] argsArg, Object kwArgs, PyObjectFormat formatNode) { this.args = argsArg; this.keywords = kwArgs; this.autoNumbering = 0; @@ -111,14 +110,14 @@ public TruffleString build(Node node, Object[] argsArg, Object kwArgs, FormatNod return buildString(node, 0, template.length(), 2, formatNode); } - private TruffleString buildString(Node node, int start, int end, int level, FormatNode formatNode) { + private TruffleString buildString(Node node, int start, int end, int level, PyObjectFormat formatNode) { if (level == 0) { throw PRaiseNode.raiseStatic(node, ValueError, RECURSION_DEPTH_EXCEEDED); } return doBuildString(node, start, end, level - 1, this.template, formatNode); } - private TruffleString doBuildString(Node node, int start, int end, int level, String s, FormatNode formatNode) { + private TruffleString doBuildString(Node node, int start, int end, int level, String s, PyObjectFormat formatNode) { StringBuilder out = new StringBuilder(); int lastLiteral = start; int i = start; @@ -376,7 +375,7 @@ private static int toInt(Node node, String s) { } } - private Object renderField(Node node, int start, int end, boolean recursive, int level, FormatNode formatNode) { + private Object renderField(Node node, int start, int end, boolean recursive, int level, PyObjectFormat formatNode) { Field filed = parseField(node, start, end); String name = filed.name; Character conversion = filed.conversion; @@ -411,9 +410,9 @@ private Object renderField(Node node, int start, int end, boolean recursive, int public static class FieldNameSplitResult { public Object first; - public List parserList; + public ArrayList parserList; - public FieldNameSplitResult(Object first, List parserList) { + public FieldNameSplitResult(Object first, ArrayList parserList) { this.first = first; this.parserList = parserList; } @@ -458,16 +457,16 @@ private static Object convert(Node node, Object obj, char conversion) { case 'r': return PyObjectReprAsObjectNode.executeUncached(obj); case 's': - return PyObjectStrAsTruffleStringNode.executeUncached(obj); + return PyObjectStrAsObjectNode.executeUncached(obj); case 'a': - return PyObjectAsciiNode.executeUncached(obj); + return PyObjectAsciiAsObjectNode.executeUncached(obj); default: throw PRaiseNode.raiseStatic(node, ValueError, INVALID_CONVERSION); } } @TruffleBoundary - public List formatterParser(Node node) { + public ArrayList formatterParser(Node node) { this.parserList = new ArrayList<>(); this.lastEnd = 0; buildString(node, 0, this.template.length(), 2, null); 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/struct/StructBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/StructBuiltins.java index 48faca88dc..cef73d8aa4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/StructBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/StructBuiltins.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 @@ -63,7 +63,6 @@ import static com.oracle.graal.python.nodes.ErrorMessages.UNPACK_REQ_A_BUFFER_OF_N_BYTES; import static com.oracle.graal.python.runtime.exception.PythonErrorType.StructError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import java.nio.ByteOrder; import java.util.HashSet; @@ -678,9 +677,9 @@ protected Object get(PStruct self) { public abstract static class GetStructFormat extends PythonBuiltinNode { @Specialization protected Object get(PStruct self, - @Cached TruffleString.FromByteArrayNode fromBytes, - @Cached TruffleString.SwitchEncodingNode switchEncoding) { - return switchEncoding.execute(fromBytes.execute(self.getFormat(), TruffleString.Encoding.US_ASCII), TS_ENCODING); + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromBytes) { + byte[] format = self.getFormat(); + return fromBytes.execute(format, 0, format.length, TruffleString.CompactionLevel.S1, false); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/StructNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/StructNodes.java index 1bc0414aeb..a0cf2d8d1c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/StructNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/StructNodes.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 @@ -115,8 +115,9 @@ static long get(VirtualFrame frame, Object value, boolean unsigned, // ------------------------------------------------------------------------------------------------------------ @ImportStatic({PGuards.class}) public abstract static class StructBaseNode extends PNodeWithContext { - public static final BigInteger UBYTE_MASK = BigInteger.valueOf(0xff); - public static final BigInteger ULONG_MASK = BigInteger.ONE.shiftLeft(Long.SIZE).subtract(BigInteger.ONE); + private static final BigInteger ULONG_MASK = BigInteger.ONE.shiftLeft(Long.SIZE).subtract(BigInteger.ONE); + protected static final BigInteger PTR_SIGNED_MIN = BigInteger.valueOf(Long.MIN_VALUE); + protected static final BigInteger PTR_UNSIGNED_MAX = ULONG_MASK; public static int getNumBytesLimit() { return NUM_BYTES_LIMIT; @@ -202,6 +203,18 @@ public static BigInteger getAsUnsignedBigInt(long value) { return BigInteger.valueOf(value).and(ULONG_MASK); } + @TruffleBoundary + public static long checkVoidPtr(Node raisingNode, BigInteger value) { + if (value.signum() < 0) { + if (value.compareTo(PTR_SIGNED_MIN) < 0) { + throw PRaiseNode.raiseStatic(raisingNode, StructError, ARG_O_O_RANGE); + } + } else if (value.compareTo(PTR_UNSIGNED_MAX) > 0) { + throw PRaiseNode.raiseStatic(raisingNode, StructError, ARG_O_O_RANGE); + } + return value.longValue(); + } + public static long handleSign(FormatCode code, long num) { // handle the sign extension (2's complement) long value = num; @@ -369,8 +382,14 @@ static void packObjectCached(VirtualFrame frame, FormatCode formatCode, FormatAl } packFloat(formatCode, formatAlignment, asDoubleNode.execute(frame, inliningTarget, value), buffer, offset, inliningTarget, numericSupport, raiseNode); } else if (isFmtVoidPtr(formatCode)) { - getLongNode.execute(frame, value, formatCode.isUnsigned()); - packLong(formatCode, formatAlignment, getLongNode.execute(frame, value, formatCode.isUnsigned()), buffer, offset, inliningTarget, numericSupport, profileSigned, raiseNode); + BigInteger num; + try { + num = toJavaBigIntegerNode.execute(inliningTarget, value); + } catch (PException pe) { + pe.expect(inliningTarget, PythonBuiltinClassType.TypeError, errorProfile); + throw raiseNode.raise(inliningTarget, StructError, ARG_NOT_T, "an integer"); + } + numericSupport.putLong(buffer, offset, checkVoidPtr(inliningTarget, num), formatCode.numBytes()); } else if (isFmtBoolean(formatCode)) { packBoolean(formatCode, formatAlignment, isTrueNode.execute(frame, value), buffer, offset); } else if (isFmtBytes(formatCode)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java index d57d842d38..9e8fd679a5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java @@ -90,6 +90,7 @@ import com.oracle.graal.python.nodes.bytecode.FrameInfo; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; +import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.frame.ReadFrameNode; import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode; @@ -97,7 +98,9 @@ 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.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.runtime.CallerFlags; import com.oracle.graal.python.runtime.PythonOptions; @@ -105,6 +108,7 @@ 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.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.bytecode.BytecodeFrame; import com.oracle.truffle.api.bytecode.BytecodeNode; import com.oracle.truffle.api.dsl.Bind; @@ -125,6 +129,7 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; +import com.oracle.truffle.api.profiles.InlinedIntValueProfile; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(extendClasses = PythonBuiltinClassType.Super) @@ -458,19 +463,37 @@ private Object supercheck(VirtualFrame frame, Node inliningTarget, Object cls, O @GenerateNodeFactory public abstract static class GetNode extends DescrGetBuiltinNode { @Specialization - static Object doNoneOrBound(SuperObject self, Object obj, @SuppressWarnings("unused") Object type, + static Object doNoneOrBound(VirtualFrame frame, SuperObject self, Object obj, @SuppressWarnings("unused") Object type, @Bind Node inliningTarget, @Cached InlinedConditionProfile objIsNoneProfile, @Cached InlinedConditionProfile selfObjIsNullProfile, + @Cached IsBuiltinClassExactProfile isBuiltinSuperProfile, @Cached GetObjectNode getObject, + @Cached GetTypeNode getType, + @Cached GetPythonObjectClassNode getClass, + @Cached CallNode callNode, + @Cached InlinedConditionProfile superTypeIsNullProfile, @Cached DoGetNode doGetNode) { - // TODO: (GR-53092) doesn't seem to handle super subclasses like CPython if (objIsNoneProfile.profile(inliningTarget, PGuards.isPNone(obj)) || // selfObjIsNullProfile.profile(inliningTarget, getObject.execute(inliningTarget, self) != null)) { return self; } + Object cls = getClass.execute(inliningTarget, self); + if (!isBuiltinSuperProfile.profileClass(inliningTarget, cls, PythonBuiltinClassType.Super)) { + return doSuperSubclass(frame, inliningTarget, self, obj, cls, getType, callNode, superTypeIsNullProfile); + } return doGetNode.execute(inliningTarget, self, obj); } + + @InliningCutoff + private static Object doSuperSubclass(VirtualFrame frame, Node inliningTarget, SuperObject self, Object obj, Object cls, GetTypeNode getType, CallNode callNode, + InlinedConditionProfile superTypeIsNullProfile) { + Object superType = getType.execute(inliningTarget, self); + if (superTypeIsNullProfile.profile(inliningTarget, superType == null)) { + return callNode.execute(frame, cls); + } + return callNode.execute(frame, cls, superType, obj); + } } @GenerateInline @@ -517,6 +540,7 @@ Object get(VirtualFrame frame, SuperObject self, Object attr, @Cached TruffleString.EqualNode equalNode, @Cached GetObjectTypeNode getObjectType, @Cached CastToTruffleStringChecked1Node castToTruffleStringNode, + @Cached InlinedIntValueProfile mroLenProfile, @Cached InlinedConditionProfile hasDescrGetProfile, @Cached InlinedConditionProfile getObjectIsStartObjectProfile, @Cached IsForeignObjectNode isForeignObjectNode, @@ -545,7 +569,7 @@ Object get(VirtualFrame frame, SuperObject self, Object attr, PythonAbstractClass[] mro = getMro(startType); /* No need to check the last one: it's gonna be skipped anyway. */ int i = 0; - int n = mro.length; + int n = mroLenProfile.profile(inliningTarget, mro.length); for (i = 0; i + 1 < n; i++) { if (isSameType(type, mro[i])) { break; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java index adc8aa0c17..4942e722f3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.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 @@ -72,7 +72,6 @@ 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.attributes.MergedObjectTypeModuleGetAttributeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; @@ -141,7 +140,8 @@ public abstract static class GetAttributeNode extends GetAttrBuiltinNode { * Keep in sync with * {@link com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode} * and {@link com.oracle.graal.python.builtins.objects.type.TypeBuiltins.GetattributeNode} - * and {@link MergedObjectTypeModuleGetAttributeNode} + * and + * {@link com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode} */ @Specialization Object doIt(VirtualFrame frame, PThreadLocal object, Object keyObj, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/LazyTraceback.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/LazyTraceback.java index 8de472f4bb..b4f1b9e9ca 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/LazyTraceback.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/LazyTraceback.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 @@ -43,6 +43,7 @@ import static com.oracle.graal.python.builtins.objects.traceback.PTraceback.UNKNOWN_LINE_NUMBER; import com.oracle.graal.python.builtins.objects.frame.PFrame; +import com.oracle.graal.python.builtins.objects.generator.PGenerator; import com.oracle.graal.python.nodes.bytecode.FrameInfo; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.truffle.api.CompilerDirectives; @@ -150,7 +151,7 @@ public boolean isEmptySegment() { } public static boolean elementWantedForTraceback(TruffleStackTraceElement element) { - Frame frame = element.getFrame(); + Frame frame = PGenerator.unwrapDSLGeneratorFrame(element); if (frame != null) { // only include frames of non-builtin python functions Object info = frame.getFrameDescriptor().getInfo(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/TracebackBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/TracebackBuiltins.java index 86d74e3279..3ce8a2a87d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/TracebackBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/traceback/TracebackBuiltins.java @@ -270,13 +270,19 @@ static PFrame doOnStack(VirtualFrame frame, PTraceback tb, Reference frameInfo = tb.getFrameInfo(); assert frameInfo.isEscaped() : createAssertionMessage("Cannot create traceback for non-escaped frame", frameInfo); - PFrame escapedFrame = readCallerFrame.getFrameForReference(frame, frameInfo, ReadFrameNode.AllPythonFramesSelector.INSTANCE, 0, 0); + PFrame escapedFrame = readCallerFrame.getFrameForReference(frame, frameInfo, ReadFrameNode.AllPythonFramesSelector.INSTANCE, 0, 0, getEscapedFrameThread(tb)); assert escapedFrame != null : createAssertionMessage("Failed to find escaped frame on stack", frameInfo); tb.setFrame(escapedFrame); return escapedFrame; } + private static Thread getEscapedFrameThread(PTraceback tb) { + LazyTraceback lazyTraceback = tb.getLazyTraceback(); + PException exception = lazyTraceback != null ? lazyTraceback.getException() : null; + return exception != null ? exception.getEscapedFrameThread() : null; + } + @TruffleBoundary private static String createAssertionMessage(String prefix, Reference frameInfo) { String stackTrace; @@ -285,10 +291,16 @@ private static String createAssertionMessage(String prefix, Reference frameInfo) } catch (Throwable ex) { stackTrace = "Exception while getting the Python stack trace: " + ex; } - boolean isCurrentThread = frameInfo.getPyFrame().getThread() != null && - frameInfo.getPyFrame().getThread() != Thread.currentThread(); - return String.format("%s. Frame reference: root node='%s', is current thread=%b. Current stack trace:\n%s.", - prefix, frameInfo.getRootNode(), isCurrentThread, stackTrace); + String threadComment = "on unknown thread (frame not materialized)"; + if (frameInfo.getPyFrame() != null) { + if (frameInfo.getPyFrame().getThread() == Thread.currentThread()) { + threadComment = "on current thread"; + } else { + threadComment = "on thread " + frameInfo.getPyFrame().getThread().getName(); + } + } + return String.format("%s. Frame reference: root node='%s', %s. Current stack trace:\n%s.", + prefix, frameInfo.getRootNode(), threadComment, stackTrace); } // case 3: there is no PFrame[Ref], we need to take the top frame from the Truffle 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/InstantiableStructSequenceBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/InstantiableStructSequenceBuiltins.java index 1f6054487c..30912d0891 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/InstantiableStructSequenceBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/InstantiableStructSequenceBuiltins.java @@ -1,5 +1,5 @@ /* - * 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 @@ -90,7 +90,8 @@ PythonBuiltinClassType.PIntInfo, PythonBuiltinClassType.PHashInfo, PythonBuiltinClassType.PThreadInfo, - PythonBuiltinClassType.PUnraisableHookArgs}) + PythonBuiltinClassType.PUnraisableHookArgs, + PythonBuiltinClassType.PExceptHookArgs}) public class InstantiableStructSequenceBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = InstantiableStructSequenceBuiltinsSlotsGen.SLOTS; 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..968507476a 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 @@ -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 @@ -81,7 +81,6 @@ import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.strings.TruffleString; @@ -204,7 +203,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); @@ -224,8 +223,8 @@ private static void copyMethod(PythonLanguage language, PythonAbstractClass klas } private static void createMember(PythonLanguage language, Object klass, TruffleString name, TruffleString doc, int idx) { - RootCallTarget callTarget = language.createStructSeqIndexedMemberAccessCachedCallTarget((l) -> new GetStructMemberNode(l, idx), idx); - PBuiltinFunction getter = PFactory.createBuiltinFunction(language, name, klass, 0, 0, callTarget); + GetStructMemberNode rootNode = language.createStructSeqIndexedMemberAccessCachedRootNode((l) -> new GetStructMemberNode(l, idx), idx); + PBuiltinFunction getter = PFactory.createBuiltinFunction(language, name, klass, 0, 0, rootNode); GetSetDescriptor callable = PFactory.createGetSetDescriptor(language, getter, null, name, klass, false); if (doc != null) { callable.setAttribute(T___DOC__, doc); 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 8644f8e07a..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 @@ -1,5 +1,5 @@ /* - * 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 @@ -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; @@ -116,7 +116,8 @@ PythonBuiltinClassType.PIntInfo, PythonBuiltinClassType.PHashInfo, PythonBuiltinClassType.PThreadInfo, - PythonBuiltinClassType.PUnraisableHookArgs}) + PythonBuiltinClassType.PUnraisableHookArgs, + PythonBuiltinClassType.PExceptHookArgs}) public final class StructSequenceBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = StructSequenceBuiltinsSlotsGen.SLOTS; @@ -166,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/PythonClass.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonClass.java index 1838052635..6eaf79c37b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonClass.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonClass.java @@ -322,7 +322,7 @@ private void updateMroShapeSubTypes(PythonLanguage lang) { break; } PythonClass subType = subTypeRef.get(); - if (subType != null) { + if (subType != null && subType.hasMroShapeSubTypes()) { subType.mroShape = MroShape.create(subType.getMethodResolutionOrder(), lang); } } 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 9a5692e6b7..e86fceafa4 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 @@ -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. @@ -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.PythonToNativeInternalNode; 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; @@ -69,13 +70,18 @@ public abstract class PythonManagedClass extends PythonObject implements PythonA private boolean abstractClass; private final PDict subClasses; + /** + * This field is positioned to be at the same offset as the one in + * {@link com.oracle.graal.python.builtins.PythonBuiltinClassType#slots}. I found that the + * compiler in the lower tier will then unify the diamond because it's actually reading at the + * same offset from the object pointer, and that gave a small but measurable speedup. + */ + protected TpSlots tpSlots; @CompilationFinal private Shape instanceShape; private TruffleString name; private TruffleString qualName; private int indexedSlotCount; - protected TpSlots tpSlots; - /** {@code true} if the MRO contains a native class. */ private final boolean needsNativeAllocation; @CompilationFinal private boolean mroInitialized = false; @@ -279,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(), + PythonToNativeInternalNode.executeUncached(base, false)); + } catch (Throwable t) { + throw CompilerDirectives.shouldNotReachHere(t); + } } GetSubclassesNode.addSubclass(base, this); } @@ -363,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 d340d9bc39..4e99840aa5 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; @@ -229,10 +229,7 @@ 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.nodes.DenyReplace; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.UnadoptableNode; @@ -275,10 +272,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 +446,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 +466,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 +762,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 +770,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 +778,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 +794,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 +883,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 +930,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 +1036,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 +1048,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 +1150,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 +1208,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 +1228,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 +1271,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 +1316,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 +1334,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 +1373,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 +1386,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 +1412,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 +1542,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 +1667,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 +1681,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) { @@ -1720,6 +1696,7 @@ public static void setSlots(PythonAbstractClass klass, TpSlots slots) { } public static void initializeBuiltinSlots(PythonLanguage language) { + TpSlotIterNext.initNextNotImplementedCallTarget(language); for (PythonBuiltinClassType klass : PythonBuiltinClassType.VALUES) { for (TpSlotMeta slotMeta : TpSlotMeta.VALUES) { TpSlot slotValue = slotMeta.getValue(klass.getDeclaredSlots()); @@ -1973,14 +1950,19 @@ public static TpSlots executeUncached(Object pythonClass) { return GetTpSlotsNodeGen.getUncached().execute(null, pythonClass); } + /* + * On the fast path this most likely comes from GetCachedTpSlotsNode, which already checked + * PythonBuiltinClassType as specialization before, so in this node we prefer to check for + * PythonManagedClass first. + */ @Specialization - static TpSlots doBuiltinType(PythonBuiltinClassType type) { - return type.getSlots(); + static TpSlots doManaged(PythonManagedClass klass) { + return klass.getTpSlots(); } @Specialization - static TpSlots doManaged(PythonManagedClass klass) { - return klass.getTpSlots(); + static TpSlots doBuiltinType(PythonBuiltinClassType type) { + return type.getSlots(); } @Specialization @@ -2008,8 +1990,8 @@ private static TpSlots initializeNativeSlots(PythonAbstractNativeObject nativeKl } } - @GenerateInline(inlineByDefault = true) - @GenerateCached + @GenerateInline + @GenerateCached(false) @ImportStatic(PGuards.class) public abstract static class GetCachedTpSlotsNode extends Node { public abstract TpSlots execute(Node inliningTarget, Object pythonClass); @@ -2042,25 +2024,16 @@ public static GetCachedTpSlotsNode getUncached() { } } - @GenerateInline(inlineByDefault = true) - @GenerateCached + @GenerateInline + @GenerateCached(false) @GenerateUncached public abstract static class GetObjectSlotsNode extends Node { public abstract TpSlots execute(Node inliningTarget, Object pythonObject); - public final TpSlots executeCached(Object pythonObject) { - return execute(this, pythonObject); - } - public static TpSlots executeUncached(Object pythonObject) { return GetObjectSlotsNodeGen.getUncached().execute(null, pythonObject); } - @NeverDefault - public static GetObjectSlotsNode create() { - return GetObjectSlotsNodeGen.create(); - } - // Note: it seems that switching the GetClassNode with an adhoc GetClassNode variant that // does not have any inline caches does not change peak at least for micro:if-polymorph // TODO: verify this on all benchmarks and get rid of the IC if possible 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 b97416bddd..a7ffa2ac9f 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__; @@ -72,6 +73,7 @@ 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.toTruffleStringUncached; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.util.Arrays; import java.util.List; @@ -131,6 +133,7 @@ import com.oracle.graal.python.lib.PyObjectIsTrueNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PConstructAndRaiseNode; @@ -140,10 +143,10 @@ import com.oracle.graal.python.nodes.SpecialAttributeNames; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; -import com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.ConstructTupleNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; @@ -187,6 +190,7 @@ @CoreFunctions(extendClasses = PythonBuiltinClassType.PythonClass) public final class TypeBuiltins extends PythonBuiltins { public static final TpSlots SLOTS = TypeBuiltinsSlotsGen.SLOTS; + private static final TruffleString T_OBJECT_SETATTR = tsLiteral("object.__setattr__"); @Override protected List> getNodeFactories() { @@ -413,22 +417,30 @@ public static TypeNode create() { } @Specialization(guards = {"!isNoValue(bases)", "!isNoValue(dict)"}) - Object typeGeneric(VirtualFrame frame, Object cls, Object name, Object bases, Object dict, PKeyword[] kwds, + static Object typeGeneric(VirtualFrame frame, Object cls, Object name, Object bases, Object dict, PKeyword[] kwds, @Bind Node inliningTarget, @Cached TypeNode nextTypeNode, @Cached PRaiseNode raiseNode, + @Cached PyTupleCheckNode tupleCheck, + @Cached ConstructTupleNode constructTupleNode, @Exclusive @Cached IsTypeNode isTypeNode) { + Object basesTuple; if (!(name instanceof TruffleString || name instanceof PString)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.MUST_BE_STRINGS_NOT_P, "type() argument 1", name); - } else if (!(bases instanceof PTuple)) { + } else if (bases instanceof PTuple) { + basesTuple = bases; + } else if (tupleCheck.execute(inliningTarget, bases)) { + basesTuple = constructTupleNode.execute(frame, bases); + } else { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.MUST_BE_STRINGS_NOT_P, "type() argument 2", bases); - } else if (!(dict instanceof PDict)) { + } + if (!(dict instanceof PDict)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.MUST_BE_STRINGS_NOT_P, "type() argument 3", dict); } else if (!isTypeNode.execute(inliningTarget, cls)) { // TODO: this is actually allowed, deal with it throw raiseNode.raise(inliningTarget, NotImplementedError, ErrorMessages.CREATING_CLASS_NON_CLS_META_CLS); } - return nextTypeNode.execute(frame, cls, name, bases, dict, kwds); + return nextTypeNode.execute(frame, cls, name, basesTuple, dict, kwds); } private TypeNodes.IsAcceptableBaseNode ensureIsAcceptableBaseNode() { @@ -537,7 +549,8 @@ public abstract static class GetattributeNode extends GetAttrBuiltinNode { * {@link com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode} * and * {@link com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins.GetAttributeNode} - * and {@link MergedObjectTypeModuleGetAttributeNode} + * and + * {@link com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode} */ @Specialization protected Object doIt(VirtualFrame frame, Object object, Object keyObj, @@ -673,9 +686,11 @@ static Object getBases(Object self, @SuppressWarnings("unused") PNone value, return PFactory.createTuple(language, getBaseClassesNode.execute(inliningTarget, self)); } - @Specialization - static Object setBases(VirtualFrame frame, PythonClass cls, PTuple value, + @Specialization(guards = "tupleCheck.execute(inliningTarget, value)", limit = "1") + static Object setBases(VirtualFrame frame, PythonClass cls, Object value, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck, + @Cached ConstructTupleNode constructTupleNode, @Cached GetObjectArrayNode getArray, @Cached GetBaseClassNode getBase, @Cached GetBestBaseClassNode getBestBase, @@ -686,7 +701,8 @@ static Object setBases(VirtualFrame frame, PythonClass cls, PTuple value, @Cached GetMroNode getMroNode, @Cached PRaiseNode raiseNode) { - Object[] a = getArray.execute(inliningTarget, value); + PTuple bases = value instanceof PTuple ? (PTuple) value : constructTupleNode.execute(frame, value); + Object[] a = getArray.execute(inliningTarget, bases); if (a.length == 0) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.CAN_ONLY_ASSIGN_NON_EMPTY_TUPLE_TO_P, cls); } @@ -743,9 +759,10 @@ private static boolean typeIsSubtypeBaseChain(Node inliningTarget, Object a, Obj return (isSameTypeNode.execute(inliningTarget, b, PythonBuiltinClassType.PythonObject)); } - @Specialization(guards = "!isPTuple(value)") + @Specialization(guards = "!tupleCheck.execute(inliningTarget, value)", limit = "1") static Object setObject(@SuppressWarnings("unused") PythonClass cls, @SuppressWarnings("unused") Object value, - @Bind Node inliningTarget) { + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CAN_ONLY_ASSIGN_S_TO_S_S_NOT_P, "tuple", GetNameNode.executeUncached(cls), "__bases__", value); } @@ -853,10 +870,10 @@ abstract static class AbstractSlotNode extends PythonBinaryBuiltinNode { @GenerateInline @GenerateCached(false) abstract static class CheckSetSpecialTypeAttrNode extends Node { - abstract void execute(Node inliningTarget, Object type, Object value, TruffleString name); + abstract void execute(VirtualFrame frame, Node inliningTarget, Object type, Object value, TruffleString name); @Specialization - static void check(Node inliningTarget, Object type, Object value, TruffleString name, + static void check(VirtualFrame frame, Node inliningTarget, Object type, Object value, TruffleString name, @Cached PRaiseNode raiseNode, @Cached(inline = false) GetTypeFlagsNode getTypeFlagsNode, @Cached SysModuleBuiltins.AuditNode auditNode) { @@ -867,7 +884,7 @@ static void check(Node inliningTarget, Object type, Object value, TruffleString // Sic, it's not immutable, but CPython has this message throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.CANT_DELETE_ATTRIBUTE_S_OF_IMMUTABLE_TYPE_N, name, type); } - auditNode.audit(inliningTarget, "object.__setattr__", type, name, value); + auditNode.audit(frame, inliningTarget, T_OBJECT_SETATTR, type, name, value); } } @@ -894,7 +911,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 +918,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); @@ -920,7 +937,7 @@ static Object setName(VirtualFrame frame, Object cls, Object value, @Cached TruffleString.IndexOfCodePointNode indexOfCodePointNode, @Cached SetNameInnerNode innerNode, @Cached PRaiseNode raiseNode) { - check.execute(inliningTarget, cls, value, T___NAME__); + check.execute(frame, inliningTarget, cls, value, T___NAME__); TruffleString string; try { string = castToTruffleStringNode.execute(inliningTarget, value); @@ -978,10 +995,11 @@ static Object getModule(PythonAbstractNativeObject cls, @SuppressWarnings("unuse @Shared @Cached GetTypeFlagsNode getFlags, @Cached CStructAccess.ReadCharPtrNode getTpNameNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, - @Cached TruffleString.IndexOfCodePointNode indexOfCodePointNode, + @Cached TruffleString.LastIndexOfCodePointNode indexOfCodePointNode, @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) { @@ -992,11 +1010,11 @@ static Object getModule(PythonAbstractNativeObject cls, @SuppressWarnings("unuse // 'tp_name' contains the fully-qualified name, i.e., 'module.A.B...' TruffleString tpName = getTpNameNode.readFromObj(cls, PyTypeObject__tp_name); int len = codePointLengthNode.execute(tpName, TS_ENCODING); - int firstDot = indexOfCodePointNode.execute(tpName, '.', 0, len, TS_ENCODING); - if (firstDot < 0) { + int lastDot = indexOfCodePointNode.execute(tpName, '.', len, 0, TS_ENCODING); + if (lastDot < 0) { return T_BUILTINS; } - return substringNode.execute(tpName, 0, firstDot, TS_ENCODING, true); + return substringNode.execute(tpName, 0, lastDot, TS_ENCODING, true); } } @@ -1066,18 +1084,19 @@ 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); } } @Specialization(guards = "!isNoValue(value)") - static Object setName(Object cls, Object value, + static Object setName(VirtualFrame frame, Object cls, Object value, @Bind Node inliningTarget, @Cached CheckSetSpecialTypeAttrNode check, @Cached CastToTruffleStringNode castToStringNode, @Cached SetQualNameInnerNode innerNode, @Cached PRaiseNode raiseNode) { - check.execute(inliningTarget, cls, value, T___QUALNAME__); + check.execute(frame, inliningTarget, cls, value, T___QUALNAME__); TruffleString stringValue; try { stringValue = castToStringNode.execute(inliningTarget, value); @@ -1238,8 +1257,9 @@ public static void dir(PSet names, Object klass) { updateSingleNode.execute(null, names, ns); } Object basesAttr = PyObjectLookupAttr.executeUncached(klass, T___BASES__); - if (basesAttr instanceof PTuple) { - Object[] bases = ToArrayNode.executeUncached(((PTuple) basesAttr).getSequenceStorage()); + if (basesAttr instanceof PTuple || PyTupleCheckNode.executeUncached(basesAttr)) { + PTuple basesTuple = basesAttr instanceof PTuple ? (PTuple) basesAttr : ConstructTupleNode.getUncached().execute(null, basesAttr); + Object[] bases = ToArrayNode.executeUncached(basesTuple.getSequenceStorage()); for (Object cls : bases) { // Note that since we are only interested in the keys, the order // we merge classes is unimportant @@ -1419,7 +1439,7 @@ static Object signature(TruffleString name, TruffleString internalDoc) { static int findSignature(String n, String doc) { String name = n; /* for dotted names like classes, only use the last component */ - int dot = n.indexOf('.'); + int dot = n.lastIndexOf('.'); if (dot != -1) { name = name.substring(dot + 1); } 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 944f037bfa..960f28b93f 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,9 @@ 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.CFields.PyTupleObject__ob_item; +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 +110,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.PythonToNativeInternalNode; 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 +209,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; @@ -222,7 +229,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; -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; @@ -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, PythonToNativeInternalNode.executeUncached(obj, false)); 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); } } @@ -951,14 +961,26 @@ static PythonAbstractClass[] doNative(Node inliningTarget, PythonNativeClass obj } catch (ClassCastException e) { throw raise.raise(inliningTarget, PythonBuiltinClassType.SystemError, ErrorMessages.UNSUPPORTED_OBJ_IN, "tp_bases"); } + } else if (result instanceof PythonAbstractNativeObject nativeTuple) { + int length = Math.toIntExact(readLongField(nativeTuple.getPtr(), CFields.PyVarObject__ob_size)); + Object[] values = getTpBasesNode.readPyObjectArray(CStructAccess.getFieldPtr(nativeTuple.getPtr(), PyTupleObject__ob_item), length); + try { + return cast(values, length); + } catch (ClassCastException e) { + throw raise.raise(inliningTarget, PythonBuiltinClassType.SystemError, ErrorMessages.UNSUPPORTED_OBJ_IN, "tp_bases"); + } } throw raise.raise(inliningTarget, PythonBuiltinClassType.SystemError, ErrorMessages.TYPE_DOES_NOT_PROVIDE_BASES); } // TODO: get rid of this private static PythonAbstractClass[] cast(Object[] arr, SequenceStorage storage) { - PythonAbstractClass[] bases = new PythonAbstractClass[storage.length()]; - for (int i = 0; i < storage.length(); i++) { + return cast(arr, storage.length()); + } + + private static PythonAbstractClass[] cast(Object[] arr, int length) { + PythonAbstractClass[] bases = new PythonAbstractClass[length]; + for (int i = 0; i < length; i++) { bases[i] = (PythonAbstractClass) arr[i]; } return bases; @@ -1275,9 +1297,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 +1520,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 +1590,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 +1795,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 +1803,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; @@ -2318,9 +2340,9 @@ private static void addDictDescrAttribute(PythonAbstractClass[] basesArray, Pyth // initialized yet if ((!hasPythonClassBases(basesArray) && LookupAttributeInMRONode.lookupSlowPath(pythonClass, T___DICT__) == PNone.NO_VALUE) || basesHaveSlots(basesArray)) { Builtin dictBuiltin = ObjectBuiltins.DictNode.class.getAnnotation(Builtin.class); - RootCallTarget callTarget = PythonLanguage.get(null).createCachedCallTarget( + BuiltinFunctionRootNode rootNode = PythonLanguage.get(null).createCachedRootNode( l -> new BuiltinFunctionRootNode(l, dictBuiltin, ObjectBuiltinsFactory.DictNodeFactory.getInstance(), true), ObjectBuiltins.DictNode.class); - setAttribute(T___DICT__, dictBuiltin, callTarget, pythonClass, language); + setAttribute(T___DICT__, dictBuiltin, rootNode, pythonClass, language); } } @@ -2328,15 +2350,15 @@ private static void addDictDescrAttribute(PythonAbstractClass[] basesArray, Pyth private static void addWeakrefDescrAttribute(PythonClass pythonClass, PythonLanguage language) { if (LookupAttributeInMRONode.lookupSlowPath(pythonClass, T___WEAKREF__) == PNone.NO_VALUE) { Builtin builtin = GetWeakRefsNode.class.getAnnotation(Builtin.class); - RootCallTarget callTarget = PythonLanguage.get(null).createCachedCallTarget( + BuiltinFunctionRootNode rootNode = PythonLanguage.get(null).createCachedRootNode( l -> new BuiltinFunctionRootNode(l, builtin, WeakRefModuleBuiltinsFactory.GetWeakRefsNodeFactory.getInstance(), true), GetWeakRefsNode.class); - setAttribute(T___WEAKREF__, builtin, callTarget, pythonClass, language); + setAttribute(T___WEAKREF__, builtin, rootNode, pythonClass, language); } } - private static void setAttribute(TruffleString name, Builtin builtin, RootCallTarget callTarget, PythonClass pythonClass, PythonLanguage language) { - int flags = PBuiltinFunction.getFlags(builtin, callTarget); - PBuiltinFunction function = PFactory.createBuiltinFunction(language, name, pythonClass, 1, flags, callTarget); + private static void setAttribute(TruffleString name, Builtin builtin, BuiltinFunctionRootNode rootNode, PythonClass pythonClass, PythonLanguage language) { + int flags = PBuiltinFunction.getFlags(builtin, rootNode.getSignature()); + PBuiltinFunction function = PFactory.createBuiltinFunction(language, name, pythonClass, 1, flags, rootNode); GetSetDescriptor desc = PFactory.createGetSetDescriptor(language, function, function, name, pythonClass, true); pythonClass.setAttribute(name, desc); } @@ -2412,7 +2434,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; @@ -2603,7 +2625,7 @@ private static int getBuiltinTypeItemsize(PythonBuiltinClassType cls) { case PInt, Boolean -> 4; case PAsyncGenerator, PFlags, PHashInfo, PTuple, PCoroutine, PGenerator, PThreadInfo, PMemoryView, PStatResult, PUnameResult, PStructTime, PFloatInfo, PStatvfsResult, PIntInfo, PFrame, - PTerminalSize, PUnraisableHookArgs -> 8; + PTerminalSize, PUnraisableHookArgs, PExceptHookArgs -> 8; case PythonClass -> 40; default -> 0; }; @@ -2788,6 +2810,7 @@ static boolean check(Node inliningTarget, Object type, /** Equivalent of CPython's {@code recursive_isinstance} */ @GenerateInline @GenerateCached(false) + @GenerateUncached public abstract static class GenericInstanceCheckNode extends Node { public abstract boolean execute(VirtualFrame frame, Node inliningTarget, Object instance, Object cls); @@ -2838,6 +2861,7 @@ static boolean isInstance(VirtualFrame frame, Node inliningTarget, Object instan /** Equivalent of CPython's {@code recursive_issubclass} */ @GenerateInline @GenerateCached(false) + @GenerateUncached public abstract static class GenericSubclassCheckNode extends Node { public abstract boolean execute(VirtualFrame frame, Node inliningTarget, Object derived, Object cls); 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..ce71b0569c 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); } } @@ -324,10 +309,10 @@ final PBuiltinFunction createBuiltin(Python3Core core, Object type, TruffleStrin Class nodeClass = NodeFactoryBase.getWrappedNodeClass(factory); validateSlotNode(factory, nodeClass, slotSignature); PythonBuiltinClassType builtinType = type instanceof PythonBuiltinClassType bt ? bt : null; - RootCallTarget callTarget = core.getLanguage().createCachedCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, true, builtinType), factory.getNodeClass(), nodeClass, - builtinType, name); + BuiltinFunctionRootNode rootNode = core.getLanguage().createCachedRootNode( + l -> new BuiltinFunctionRootNode(l, builtin, factory, true, builtinType), factory.getNodeClass(), nodeClass, builtinType, name); - PBuiltinFunction function = PFactory.createWrapperDescriptor(core.getLanguage(), tsName, type, numDefaults(builtin), 0, callTarget, this, wrapper); + PBuiltinFunction function = PFactory.createWrapperDescriptor(core.getLanguage(), tsName, type, numDefaults(builtin), 0, rootNode, this, wrapper); function.setAttribute(T___DOC__, SlotWrapperDocstrings.getDocstring(name)); return function; } @@ -337,12 +322,12 @@ final PBuiltinFunction createBuiltin(Python3Core core, Object type, TruffleStrin * {@link #initialize(PythonLanguage)} to create the slot call target if the slot node can * be wrapped by {@link BuiltinFunctionRootNode}. */ - static RootCallTarget createSlotCallTarget(PythonLanguage language, BuiltinSlotWrapperSignature signature, NodeFactory factory, String name) { + static BuiltinFunctionRootNode createSlotRootNode(PythonLanguage language, BuiltinSlotWrapperSignature signature, NodeFactory factory, String name) { SlotSignature slotSignature = factory.getNodeClass().getAnnotation(SlotSignature.class); Builtin builtin = new Slot2Builtin(slotSignature, name, signature); Class nodeClass = NodeFactoryBase.getWrappedNodeClass(factory); validateSlotNode(factory, nodeClass, slotSignature); - return language.createCachedCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, true), factory.getNodeClass(), nodeClass, name); + return language.createCachedRootNode(l -> new BuiltinFunctionRootNode(l, builtin, factory, true), factory.getNodeClass(), nodeClass, name); } private static void validateSlotNode(NodeFactory factory, Class nodeClass, SlotSignature slotSignature) { 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..1625dd90d0 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.PythonToNativeNode; +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.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; @@ -93,8 +99,7 @@ final PythonBinaryBuiltinNode createSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget target = createSlotCallTarget(language, BuiltinSlotWrapperSignature.BINARY, getNodeFactory(), builtinName); - language.setBuiltinSlotCallTarget(callTargetIndex, target); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, BuiltinSlotWrapperSignature.BINARY, getNodeFactory(), builtinName)); } } @@ -127,6 +132,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 +153,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(inline = false) PythonToNativeNode selfToNativeNode, - @Cached(inline = false) PythonToNativeNode argToNativeNode, - @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode, - @Cached(inline = false) NativeToPythonTransferNode toPythonNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode selfToNativeNode, + @Cached PythonToNativeInternalNode argToNativeNode, + @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.execute(inliningTarget, promotedSelf), argToNativeNode.execute(inliningTarget, promotedArg)); + return checkResultNode.execute(state, T_BINARY_SLOT, toPythonNode.executeTransfer(inliningTarget, lresult)); + } 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..dd8fdba2e5 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,17 +69,19 @@ 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.PythonToNativeNode; +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.PArguments; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.function.PFunction; @@ -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; @@ -241,8 +246,7 @@ final BinaryOpBuiltinNode createOpSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, BuiltinSlotWrapperSignature.BINARY, getNodeFactory(), builtinName); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, BuiltinSlotWrapperSignature.BINARY, getNodeFactory(), builtinName)); } @Override @@ -345,6 +349,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 +374,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(inline = false) PythonToNativeNode selfToNativeNode, - @Cached(inline = false) PythonToNativeNode argToNativeNode, - @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode, - @Cached(inline = false) NativeToPythonTransferNode toPythonNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode selfToNativeNode, + @Cached PythonToNativeInternalNode argToNativeNode, + @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.execute(inliningTarget, promotedSelf), argToNativeNode.execute(inliningTarget, promotedArg)); + return checkResultNode.execute(state, op.name, toPythonNode.executeTransfer(inliningTarget, lresult)); + } 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 505c6954e0..93de696524 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 @@ -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,16 +44,19 @@ 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.PythonToNativeNode; +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.PArguments; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.function.PFunction; @@ -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; @@ -145,8 +149,7 @@ protected TpSlotDescrGetBuiltinComplex(NodeFactory nodeFactory) { public void initialize(PythonLanguage language) { // We need a different call-target for the "raw" slot. It must not normalize None to // NO_VALUE (NULL in CPython) like the __get__ wrapper. - RootCallTarget callTarget = createSlotCallTarget(language, SIGNATURE, getNodeFactory(), J___GET__); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SIGNATURE, getNodeFactory(), J___GET__)); } } @@ -160,14 +163,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 +196,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(); @@ -215,22 +219,33 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi return dispatcherNode.execute(frame, inliningTarget, slot.getCallable(), slot.getType(), self, obj, type); } + @InliningCutoff @Specialization static Object 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) NativeToPythonTransferNode toPythonNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode selfToNativeNode, + @Cached PythonToNativeInternalNode objToNativeNode, + @Cached PythonToNativeInternalNode valueToNativeNode, + @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.execute(inliningTarget, promotedSelf), // + objToNativeNode.execute(inliningTarget, promotedObj), // + valueToNativeNode.execute(inliningTarget, promotedValue)); + return checkResultNode.execute(threadState, T___GET__, toPythonNode.executeTransfer(inliningTarget, lresult)); + } finally { + Reference.reachabilityFence(promotedSelf); + Reference.reachabilityFence(promotedObj); + Reference.reachabilityFence(promotedValue); + } } @TruffleBoundary @@ -298,8 +313,8 @@ protected static Object doCachedPFunction(VirtualFrame frame, Node inliningTarge return invoke.execute(frame, inliningTarget, callNode, cachedCallee, arguments); } - private static Object normalizeNoValue(InlinedConditionProfile profile, Node inlinintTarget, Object o) { - return profile.profile(inlinintTarget, o == PNone.NO_VALUE) ? PNone.NONE : o; + private static Object normalizeNoValue(InlinedConditionProfile profile, Node inliningTarget, Object o) { + return profile.profile(inliningTarget, o == PNone.NO_VALUE) ? PNone.NONE : o; } @Specialization(replaces = "doCachedPFunction") 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 f2010e0ca9..23f34fab69 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 @@ -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 @@ -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; @@ -113,8 +118,7 @@ final DescrSetBuiltinNode createSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget target = createSlotCallTarget(language, SET_SIGNATURE, getNodeFactory(), J___SET__); - language.setBuiltinSlotCallTarget(callTargetIndex, target); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SET_SIGNATURE, getNodeFactory(), J___SET__)); } @Override @@ -193,21 +197,32 @@ 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), // + objToNativeNode.execute(inliningTarget, promotedObj), // + valueToNativeNode.execute(inliningTarget, promotedValue)); + checkResultNode.executeInt(inliningTarget, threadState, T___SET__, iresult); + } finally { + Reference.reachabilityFence(promotedSelf); + Reference.reachabilityFence(promotedObj); + Reference.reachabilityFence(promotedValue); + } } } + @InliningCutoff private static PException raiseAttributeError(Node inliningTarget, PRaiseNode raiseNode, TruffleString attrName) { return raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError, attrName); } @@ -225,6 +240,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..6032694823 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.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.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; @@ -111,8 +120,7 @@ final GetAttrBuiltinNode createSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget target = createSlotCallTarget(language, BuiltinSlotWrapperSignature.BINARY, getNodeFactory(), J___GETATTRIBUTE__); - language.setBuiltinSlotCallTarget(callTargetIndex, target); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, BuiltinSlotWrapperSignature.BINARY, getNodeFactory(), J___GETATTRIBUTE__)); } } @@ -235,6 +243,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 +251,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 PythonToNativeNode selfToNativeNode, - @Cached NativeToPythonTransferNode toPythonNode, - @Cached ExternalFunctionInvokeNode externalInvokeNode, + @Cached PythonToNativeInternalNode nameToNativeNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode selfToNativeNode, + @Cached NativeToPythonInternalNode toPythonNode, + @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.execute(inliningTarget, 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.execute(inliningTarget, 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.executeTransfer(inliningTarget, lresult)); } } @@ -283,6 +301,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..bfb5303d02 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,13 +43,16 @@ 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.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; import com.oracle.graal.python.builtins.objects.function.PArguments; import com.oracle.graal.python.builtins.objects.ints.IntBuiltins; import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotMeta; @@ -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 { @@ -129,8 +134,7 @@ HashBuiltinNode createSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, SIGNATURE, getNodeFactory(), J___HASH__); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SIGNATURE, getNodeFactory(), J___HASH__)); } } @@ -152,6 +156,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, @@ -168,13 +177,20 @@ static long callPython(VirtualFrame frame, TpSlotPythonSingle slot, Object self, @Specialization 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 PythonToNativeInternalNode toNativeNode, + @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.execute(inliningTarget, 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..a5609e06da 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() { @@ -115,8 +123,7 @@ protected TpSlotInquiryBuiltinComplex(NodeFactory nodeFactory) { @Override public final void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, SIGNATURE, getNodeFactory(), J___BOOL__); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SIGNATURE, getNodeFactory(), J___BOOL__)); } } @@ -143,6 +150,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 +171,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)); + return checkResultNode.executeBool(inliningTarget, threadState, T___BOOL__, iresult); + } finally { + Reference.reachabilityFence(promotedSelf); + } } @Specialization(replaces = "callCachedBuiltin") @@ -209,4 +228,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 1a76d7d7a3..ea8d304d4b 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 @@ -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 @@ -41,19 +41,21 @@ 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.PythonToNativeNode; +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; import com.oracle.graal.python.builtins.objects.function.PArguments; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; @@ -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; @@ -91,6 +97,10 @@ private TpSlotIterNext() { /** Equivalent of {@code _PyObject_NextNotImplemented} */ public static final TpSlot NEXT_NOT_IMPLEMENTED = TpSlotIterNextSlotsGen.SLOTS.tp_iternext(); + public static void initNextNotImplementedCallTarget(PythonLanguage language) { + ((TpSlotIterNext.TpSlotIterNextBuiltin) NEXT_NOT_IMPLEMENTED).initialize(language); + } + public abstract static class TpSlotIterNextBuiltin extends TpSlotBuiltin { final int callTargetIndex = TpSlotBuiltinCallTargetRegistry.getNextCallTargetIndex(); @@ -104,8 +114,7 @@ final PythonUnaryBuiltinNode createSlotNode() { @Override public final void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, BuiltinSlotWrapperSignature.UNARY, getNodeFactory(), J___NEXT__); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, BuiltinSlotWrapperSignature.UNARY, getNodeFactory(), J___NEXT__)); } @Override @@ -167,6 +176,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, @@ -183,23 +197,30 @@ 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(inline = false) PythonToNativeNode toNativeNode, - @Cached ExternalFunctionInvokeNode externalInvokeNode, - @Cached(inline = false) NativeToPythonTransferNode toPythonNode, + @Cached PythonToNativeInternalNode toNativeNode, + @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.fromObject(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.execute(inliningTarget, promotedSelf)); + Object pythonResult = toPythonNode.executeTransfer(inliningTarget, 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(); + } } + 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..158dd88724 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,18 +45,22 @@ 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; +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.ints.PInt; 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.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; @@ -127,8 +133,7 @@ protected TpSlotLenBuiltinComplex(NodeFactory nodeFactory) { @Override public final void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, SIGNATURE, getNodeFactory(), J___LEN__); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SIGNATURE, getNodeFactory(), J___LEN__)); } } @@ -150,6 +155,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 +176,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(inline = false) PythonToNativeNode toNativeNode, - @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode toNativeNode, + @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.execute(inliningTarget, 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..260d445345 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,17 +45,19 @@ 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; +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.slots.PythonDispatchers.BinaryPythonSlotDispatcherNode; @@ -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; @@ -106,8 +112,7 @@ final MpAssSubscriptBuiltinNode createSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget target = createSlotCallTarget(language, SET_SIGNATURE, getNodeFactory(), J___SETITEM__); - language.setBuiltinSlotCallTarget(callTargetIndex, target); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SET_SIGNATURE, getNodeFactory(), J___SETITEM__)); } @Override @@ -179,6 +184,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 +213,28 @@ 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 PythonToNativeNode selfToNativeNode, - @Cached PythonToNativeNode keyToNativeNode, - @Cached PythonToNativeNode valueToNativeNode, - @Cached ExternalFunctionInvokeNode externalInvokeNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode selfToNativeNode, + @Cached PythonToNativeInternalNode keyToNativeNode, + @Cached PythonToNativeInternalNode valueToNativeNode, + @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.execute(inliningTarget, promotedSelf), keyToNativeNode.execute(inliningTarget, promotedKey), valueToNativeNode.execute(inliningTarget, + 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..40194c04b8 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,16 +44,19 @@ 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.PythonToNativeNode; +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.PArguments; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -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; @@ -105,8 +113,7 @@ final PythonTernaryBuiltinNode createOpSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, SIGNATURE, getNodeFactory(), J___POW__); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SIGNATURE, getNodeFactory(), J___POW__)); } @Override @@ -146,6 +153,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 +179,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(inline = false) PythonToNativeNode vToNative, - @Cached(inline = false) PythonToNativeNode wToNative, - @Cached(inline = false) PythonToNativeNode zToNative, - @Cached ExternalFunctionInvokeNode externalInvokeNode, - @Cached(inline = false) NativeToPythonTransferNode toPythonNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode vToNative, + @Cached PythonToNativeInternalNode wToNative, + @Cached PythonToNativeInternalNode zToNative, + @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.execute(inliningTarget, promotedV), wToNative.execute(inliningTarget, promotedW), zToNative.execute(inliningTarget, promotedZ)); + return checkResultNode.execute(state, T___POW__, toPythonNode.executeTransfer(inliningTarget, lresult)); + } finally { + Reference.reachabilityFence(promotedV); + Reference.reachabilityFence(promotedW); + Reference.reachabilityFence(promotedZ); + } } @SuppressWarnings("unused") @@ -236,6 +258,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 +275,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(inline = false) PythonToNativeNode vToNative, - @Cached(inline = false) PythonToNativeNode wToNative, - @Cached(inline = false) PythonToNativeNode zToNative, - @Cached ExternalFunctionInvokeNode externalInvokeNode, - @Cached(inline = false) NativeToPythonTransferNode toPythonNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode vToNative, + @Cached PythonToNativeInternalNode wToNative, + @Cached PythonToNativeInternalNode zToNative, + @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.execute(inliningTarget, promotedV), wToNative.execute(inliningTarget, promotedW), zToNative.execute(inliningTarget, promotedZ)); + return checkResultNode.execute(state, T___IPOW__, toPythonNode.executeTransfer(inliningTarget, lresult)); + } 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..c29d83bb37 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,12 +42,15 @@ 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.PythonToNativeNode; +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.PArguments; import com.oracle.graal.python.builtins.objects.function.PFunction; import com.oracle.graal.python.builtins.objects.object.ObjectNodes; @@ -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(inline = false) PythonToNativeNode toNativeNode, - @Cached ExternalFunctionInvokeNode externalInvokeNode, - @Cached(inline = false) NativeToPythonTransferNode toPythonNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode toNativeNode, + @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.execute(inliningTarget, promotedSelf)); + return checkResultNode.execute(state, T___REPR__, toPythonNode.executeTransfer(inliningTarget, 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/TpSlotRichCompare.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRichCompare.java index 6b05f997b9..83a436fda0 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,18 +43,20 @@ 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.PythonToNativeNode; +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.PArguments; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.type.slots.NodeFactoryUtils.WrapperNodeFactory; @@ -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; @@ -133,8 +137,7 @@ protected TpSlotRichCmpBuiltinComplex(NodeFactory nodeFactory) { @Override public final void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, SIGNATURE, getNodeFactory(), "tp_richcompare"); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SIGNATURE, getNodeFactory(), "tp_richcompare")); } } @@ -209,6 +212,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, @@ -241,16 +249,24 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotRichCmpP @Specialization static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object a, Object b, RichCmpOp op, @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 PythonToNativeInternalNode toNativeNodeA, + @Cached PythonToNativeInternalNode toNativeNodeB, + @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.execute(inliningTarget, promotedA), toNativeNodeB.execute(inliningTarget, promotedB), op.asNative()); + return checkResultNode.execute(state, T_TP_RICHCOMPARE, toPythonNode.executeTransfer(inliningTarget, lresult)); + } 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..42793da21c 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.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; @@ -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; @@ -116,8 +125,7 @@ final SetAttrBuiltinNode createSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget target = createSlotCallTarget(language, SET_SIGNATURE, getNodeFactory(), J___SETATTR__); - language.setBuiltinSlotCallTarget(callTargetIndex, target); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SET_SIGNATURE, getNodeFactory(), J___SETATTR__)); } @Override @@ -250,6 +258,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 +267,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 PythonToNativeNode nameToNativeNode, - @Cached PythonToNativeNode selfToNativeNode, - @Cached PythonToNativeNode valueToNativeNode, - @Cached ExternalFunctionInvokeNode externalInvokeNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode nameToNativeNode, + @Cached PythonToNativeInternalNode selfToNativeNode, + @Cached PythonToNativeInternalNode valueToNativeNode, + @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.execute(inliningTarget, 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.execute(inliningTarget, promotedSelf), nameArg, valueToNativeNode.execute(inliningTarget, 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 +329,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..83637c0f73 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,17 +42,19 @@ 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.PythonToNativeNode; +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.PArguments; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -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; @@ -107,16 +112,15 @@ final SizeArgFunBuiltinNode createSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget target = createSlotCallTarget(language, BuiltinSlotWrapperSignature.BINARY, getNodeFactory(), name); - language.setBuiltinSlotCallTarget(callTargetIndex, target); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, BuiltinSlotWrapperSignature.BINARY, getNodeFactory(), name)); } @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 -> @@ -170,7 +174,7 @@ Object doGeneric(VirtualFrame frame, Object self, Object index, CompilerDirectives.transferToInterpreterAndInvalidate(); fixNegativeIndex = insert(FixNegativeIndex.create()); } - size = fixNegativeIndex.execute(frame, size, index); + size = fixNegativeIndex.execute(frame, size, self); } return slotNode.execute(frame, self, size); } @@ -206,17 +210,17 @@ Object doGeneric(VirtualFrame frame, Object self, Object index, @GenerateInline(false) // lazy public abstract static class FixNegativeIndex extends Node { - public abstract int execute(VirtualFrame frame, int indexAsSize, Object indexObj); + public abstract int execute(VirtualFrame frame, int indexAsSize, Object self); @Specialization - static int doIt(VirtualFrame frame, int indexAsSize, Object indexObj, + static int doIt(VirtualFrame frame, int indexAsSize, Object self, @Bind Node inliningTarget, - @Cached GetObjectSlotsNode getIndexSlots, + @Cached GetObjectSlotsNode getSelfSlots, @Cached CallSlotLenNode callSlotLen) { assert indexAsSize < 0; - TpSlots indexSlots = getIndexSlots.execute(inliningTarget, indexObj); - if (indexSlots.sq_length() != null) { - int len = callSlotLen.execute(frame, inliningTarget, indexSlots.sq_length(), indexObj); + TpSlots selfSlots = getSelfSlots.execute(inliningTarget, self); + if (selfSlots.sq_length() != null) { + int len = callSlotLen.execute(frame, inliningTarget, selfSlots.sq_length(), self); return indexAsSize + len; } return indexAsSize; @@ -236,6 +240,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 +261,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(inline = false) PythonToNativeNode toNativeNode, - @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode, - @Cached(inline = false) NativeToPythonTransferNode toPythonNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode toNativeNode, + @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.execute(inliningTarget, promotedSelf), index); + return checkResultNode.execute(threadState, T___GETITEM__, toPythonNode.executeTransfer(inliningTarget, 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/TpSlotSqAssItem.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqAssItem.java index f38f2cfbef..0d82ad6732 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,17 +45,19 @@ 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; +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.slots.NodeFactoryUtils.WrapperNodeFactory; @@ -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; @@ -113,16 +119,15 @@ final SqAssItemBuiltinNode createSlotNode() { @Override public void initialize(PythonLanguage language) { - RootCallTarget target = createSlotCallTarget(language, SET_SIGNATURE, getNodeFactory(), J___SETITEM__); - language.setBuiltinSlotCallTarget(callTargetIndex, target); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, SET_SIGNATURE, getNodeFactory(), J___SETITEM__)); } @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)); @@ -169,7 +174,7 @@ Object doGeneric(VirtualFrame frame, Object self, Object index, Object value, CompilerDirectives.transferToInterpreterAndInvalidate(); fixNegativeIndex = insert(FixNegativeIndex.create()); } - size = fixNegativeIndex.execute(frame, size, index); + size = fixNegativeIndex.execute(frame, size, self); } slotNode.executeIntKey(frame, self, size, value); return PNone.NONE; @@ -204,7 +209,7 @@ Object doGeneric(VirtualFrame frame, Object self, Object index, CompilerDirectives.transferToInterpreterAndInvalidate(); fixNegativeIndex = insert(FixNegativeIndex.create()); } - size = fixNegativeIndex.execute(frame, size, index); + size = fixNegativeIndex.execute(frame, size, self); } slotNode.executeIntKey(frame, self, size, PNone.NO_VALUE); return PNone.NONE; @@ -257,6 +262,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 +291,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 PythonToNativeNode selfToNativeNode, - @Cached PythonToNativeNode valueToNativeNode, - @Cached ExternalFunctionInvokeNode externalInvokeNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode selfToNativeNode, + @Cached PythonToNativeInternalNode valueToNativeNode, + @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.execute(inliningTarget, promotedSelf), key, valueToNativeNode.execute(inliningTarget, 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..57edfc3bd4 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), + argToNativeNode.execute(inliningTarget, promotedArg)); + 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..d90d961030 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.PythonToNativeNode; +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.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; @@ -90,8 +97,7 @@ final PythonUnaryBuiltinNode createSlotNode() { @Override public final void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, BuiltinSlotWrapperSignature.UNARY, getNodeFactory(), builtinName); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, BuiltinSlotWrapperSignature.UNARY, getNodeFactory(), builtinName)); } } @@ -104,6 +110,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 +130,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(inline = false) PythonToNativeNode toNativeNode, - @Cached ExternalFunctionInvokeNode externalInvokeNode, - @Cached(inline = false) NativeToPythonTransferNode toPythonNode, + @Cached EnsurePythonObjectNode ensurePythonObjectNode, + @Cached PythonToNativeInternalNode toNativeNode, + @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.execute(inliningTarget, promotedSelf)); + return checkResultNode.execute(state, T_UNARY_SLOT, toPythonNode.executeTransferAndRelease(inliningTarget, 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/TpSlotVarargs.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java index 6063a8998b..e94c5f2bab 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,12 @@ 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.lib.PyObjectGetAttr; 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,21 +98,26 @@ 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.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.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; 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.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.ReportPolymorphism; @@ -168,8 +181,7 @@ public TruffleString getName() { @Override public final void initialize(PythonLanguage language) { - RootCallTarget callTarget = createSlotCallTarget(language, null, getNodeFactory(), name); - language.setBuiltinSlotCallTarget(callTargetIndex, callTarget); + language.setBuiltinSlotRootNode(callTargetIndex, createSlotRootNode(language, null, getNodeFactory(), name)); } @Override @@ -200,9 +212,9 @@ public PBuiltinMethod createBuiltin(Python3Core core, Object type, TruffleString * 'WrapTpNew' holds the type in a field and uses that to do the check. The dropping is * achieved by using `declaresExplicitSelf = false`. */ - RootCallTarget callTarget = language.createCachedCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, false, builtinType), + BuiltinFunctionRootNode rootNode = language.createCachedRootNode(l -> new BuiltinFunctionRootNode(l, builtin, factory, false, builtinType), nodeClass, nodeClass, builtinType, J___NEW__); - return PFactory.createNewWrapper(language, type, defaults, kwDefaults, callTarget, this); + return PFactory.createNewWrapper(language, type, defaults, kwDefaults, rootNode, this); } } @@ -306,33 +318,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); + } 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), + argsTuplePtr, + toNativeNode.execute(inliningTarget, kwargsDict)); + return checkResultNode.execute(state, name, toPythonNode.executeTransferAndRelease(inliningTarget, 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; } } @@ -342,6 +375,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 +393,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); + } + 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), + argsTuplePtr, + toNativeNode.execute(inliningTarget, kwargsDict)); + 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,21 +481,38 @@ static Object doBind(VirtualFrame frame, Node inliningTarget, Object descriptor, @ReportPolymorphism public abstract static class CallSlotTpNewNode extends CallVarargsTpSlotBaseNode { - @Specialization - static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords, + @TruffleBoundary + public static Object executeUncached(TpSlot slot, Object self, Object[] args, PKeyword[] keywords) { + return CallSlotTpNewNodeGen.getUncached().execute(null, null, slot, self, args, keywords); + } + + @Specialization(guards = "hasDefaultMetatype(inliningTarget, self, getClassNode)", limit = "1") + static Object callPythonFast(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords, + @Cached GetClassNode getClassNode, @Cached BindNewMethodNode bindNew, - @Cached(inline = false) CallNode callNode) { + @Exclusive @Cached(inline = false) CallNode callNode) { Object callable = bindNew.execute(frame, inliningTarget, slot.getCallable(), self); return callNode.execute(frame, callable, PythonUtils.prependArgument(self, args), keywords); } + @Specialization(replaces = "callPythonFast") + static Object callPython(VirtualFrame frame, Node inliningTarget, @SuppressWarnings("unused") TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords, + @Cached PyObjectGetAttr getNew, + @Exclusive @Cached(inline = false) CallNode callNode) { + Object callable = getNew.execute(frame, inliningTarget, self, T___NEW__); + return callNode.execute(frame, callable, PythonUtils.prependArgument(self, args), keywords); + } + + static boolean hasDefaultMetatype(Node inliningTarget, Object self, GetClassNode getClassNode) { + return getClassNode.execute(inliningTarget, self) == PythonBuiltinClassType.PythonClass; + } + @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 +522,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 +536,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/builtins/objects/types/UnionTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/UnionTypeBuiltins.java index 3e53b1f8a3..625397545d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/UnionTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/UnionTypeBuiltins.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 @@ -58,7 +58,6 @@ import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; @@ -76,6 +75,8 @@ import com.oracle.graal.python.lib.PyNumberOrNode; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.lib.PyObjectIsInstanceNode; +import com.oracle.graal.python.lib.PyObjectIsSubclassNode; import com.oracle.graal.python.lib.PyObjectRichCompareBool; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; @@ -225,7 +226,7 @@ abstract static class InstanceCheckNode extends PythonBinaryBuiltinNode { static boolean check(VirtualFrame frame, PUnionType self, Object other, @Bind Node inliningTarget, @Cached SequenceStorageNodes.GetItemScalarNode getItem, - @Cached BuiltinFunctions.IsInstanceNode isInstanceNode, + @Cached PyObjectIsInstanceNode isInstanceNode, @Cached PRaiseNode raiseNode) { SequenceStorage argsStorage = self.getArgs().getSequenceStorage(); boolean result = false; @@ -235,7 +236,7 @@ static boolean check(VirtualFrame frame, PUnionType self, Object other, throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ISINSTANCE_ARG_2_CANNOT_CONTAIN_A_PARAMETERIZED_GENERIC); } if (!result) { - result = isInstanceNode.executeWith(frame, other, arg); + result = isInstanceNode.execute(frame, other, arg); // Cannot break here, the check for GenericAlias needs to check all args } } @@ -251,7 +252,7 @@ static boolean check(VirtualFrame frame, PUnionType self, Object other, @Bind Node inliningTarget, @Cached TypeNodes.IsTypeNode isTypeNode, @Cached SequenceStorageNodes.GetItemScalarNode getItem, - @Cached BuiltinFunctions.IsSubClassNode isSubClassNode, + @Cached PyObjectIsSubclassNode isSubClassNode, @Cached PRaiseNode raiseNode) { if (!isTypeNode.execute(inliningTarget, other)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ISSUBCLASS_ARG_1_MUST_BE_A_CLASS); @@ -264,7 +265,7 @@ static boolean check(VirtualFrame frame, PUnionType self, Object other, throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.ISSUBCLASS_ARG_2_CANNOT_CONTAIN_A_PARAMETERIZED_GENERIC); } if (!result) { - result = isSubClassNode.executeWith(frame, other, arg); + result = isSubClassNode.execute(frame, other, arg); // Cannot break here, the check for GenericAlias needs to check all args } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BytecodeDSLCompiler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BytecodeDSLCompiler.java index 4378439ba8..91f9d66d92 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BytecodeDSLCompiler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/BytecodeDSLCompiler.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 @@ -41,6 +41,8 @@ package com.oracle.graal.python.compiler.bytecode_dsl; import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.compiler.Compiler; @@ -94,6 +96,8 @@ public static class BytecodeDSLCompilerContext { public final int futureLineNumber; public final ParserCallbacksImpl errorCallback; public final ScopeEnvironment scopeEnvironment; + // Store code units for possible reparses + public final Map codeUnits = new HashMap<>(); public BytecodeDSLCompilerContext(PythonLanguage language, ModTy mod, Source source, int optimizationLevel, EnumSet futureFeatures, int futureLineNumber, ParserCallbacksImpl errorCallback, ScopeEnvironment scopeEnvironment) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java index f39871cb04..00eafcda8e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/compiler/bytecode_dsl/RootNodeCompiler.java @@ -57,6 +57,7 @@ import static com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompilerUtils.hasDefaultArgs; import static com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompilerUtils.hasDefaultKwargs; import static com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompilerUtils.len; +import static com.oracle.graal.python.nodes.BuiltinNames.J_BREAKPOINT; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___CLASS__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___TYPE_PARAMS__; import static com.oracle.graal.python.util.PythonUtils.codePointsToInternedTruffleString; @@ -101,7 +102,6 @@ import com.oracle.graal.python.lib.PyObjectRichCompareBool; import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; -import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnitAndRoot; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNodeGen; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNodeGen.Builder; @@ -147,7 +147,6 @@ import com.oracle.graal.python.pegparser.sst.UnaryOpTy; import com.oracle.graal.python.pegparser.sst.WithItemTy; import com.oracle.graal.python.pegparser.tokenizer.SourceRange; -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.bytecode.BytecodeConfig; @@ -156,8 +155,8 @@ import com.oracle.truffle.api.bytecode.BytecodeParser; import com.oracle.truffle.api.bytecode.BytecodeRootNodes; import com.oracle.truffle.api.bytecode.serialization.BytecodeSerializer; +import com.oracle.truffle.api.debug.DebuggerTags; import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag; -import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.strings.TruffleString; /** @@ -268,6 +267,8 @@ public final class RootNodeCompiler implements BaseBytecodeDSLVisitor futureFeatures) { this(ctx, parent, null, rootNode, rootNode, futureFeatures); @@ -407,7 +408,10 @@ private String getNewScopeQualName(String name, CompilationScope scopeType) { return name; } - private BytecodeDSLCompilerResult compileRootNode(String name, ArgumentInfo argumentInfo, SourceRange sourceRange, BytecodeParser parser) { + private record CodeUnitKey(SSTNode node, CompilationScope scope) { + } + + private BytecodeDSLCompilerResult compileRootNode(String name, ArgumentInfo argumentInfo, SSTNode node, BytecodeParser parser) { qualName = getNewScopeQualName(name, scopeType); BytecodeRootNodes nodes = PBytecodeDSLRootNodeGen.create(ctx.language, BytecodeConfig.WITH_SOURCE, parser); @@ -415,57 +419,63 @@ private BytecodeDSLCompilerResult compileRootNode(String name, ArgumentInfo argu assert nodeList.size() == 1; PBytecodeDSLRootNode rootNode = nodeList.get(0); - int flags = PCode.CO_OPTIMIZED | PCode.CO_NEWLOCALS; - flags |= argumentInfo.takesVarArgs ? PCode.CO_VARARGS : 0; - flags |= argumentInfo.takesVarKeywordArgs ? PCode.CO_VARKEYWORDS : 0; - if (scope.isNested()) { - flags |= PCode.CO_NESTED; - } - if (scope.isModule()) { - flags |= PCode.CO_GRAALPYHON_MODULE; - } - if (scope.isGenerator() && scope.isCoroutine()) { - flags |= PCode.CO_ASYNC_GENERATOR; - } else if (scope.isGenerator()) { - flags |= PCode.CO_GENERATOR; - } else if (scope.isCoroutine()) { - flags |= PCode.CO_COROUTINE; - } - for (FutureFeature flag : futureFeatures) { - flags |= flag.flagValue; - } - - int classcellIndex = -1; - if (freeLocals.containsKey(J___CLASS__)) { - classcellIndex = freeLocals.get(J___CLASS__).getLocalOffset(); - } - - int selfIndex = -1; - if (argumentInfo.nonEmpty()) { - selfIndex = 0; - if (selfCellName != null) { - selfIndex = cellLocals.get(selfCellName).getLocalOffset(); - } - } - - BytecodeDSLCodeUnit codeUnit = new BytecodeDSLCodeUnit(toInternedTruffleStringUncached(name), toInternedTruffleStringUncached(qualName), - argumentInfo.argCount, argumentInfo.kwOnlyArgCount, argumentInfo.positionalOnlyArgCount, - flags, orderedTruffleStringArray(names), - orderedTruffleStringArray(varnames), - orderedTruffleStringArray(cellvars), - orderedTruffleStringArray(freevars), - cell2arg, - orderedKeys(constants, new Object[0]), - sourceRange.startLine, - sourceRange.startColumn, - sourceRange.endLine, - sourceRange.endColumn, - classcellIndex, - selfIndex, - yieldFromGenerator != null ? yieldFromGenerator.getLocalIndex() : -1, - instrumentationDataLocal.getLocalIndex(), - new BytecodeSupplier(nodes)); - rootNode.setMetadata(codeUnit, ctx.errorCallback); + CodeUnitKey key = new CodeUnitKey(node, scopeType); + BytecodeDSLCodeUnit codeUnit = ctx.codeUnits.get(key); + if (codeUnit == null) { + int flags = PCode.CO_OPTIMIZED | PCode.CO_NEWLOCALS; + flags |= argumentInfo.takesVarArgs ? PCode.CO_VARARGS : 0; + flags |= argumentInfo.takesVarKeywordArgs ? PCode.CO_VARKEYWORDS : 0; + if (scope.isNested()) { + flags |= PCode.CO_NESTED; + } + if (scope.isModule()) { + flags |= PCode.CO_GRAALPYHON_MODULE; + } + if (scope.isGenerator() && scope.isCoroutine()) { + flags |= PCode.CO_ASYNC_GENERATOR; + } else if (scope.isGenerator()) { + flags |= PCode.CO_GENERATOR; + } else if (scope.isCoroutine()) { + flags |= PCode.CO_COROUTINE; + } + for (FutureFeature flag : futureFeatures) { + flags |= flag.flagValue; + } + + int classcellIndex = -1; + if (freeLocals.containsKey(J___CLASS__)) { + classcellIndex = freeLocals.get(J___CLASS__).getLocalOffset(); + } + + int selfIndex = -1; + if (argumentInfo.nonEmpty()) { + selfIndex = 0; + if (selfCellName != null) { + selfIndex = cellLocals.get(selfCellName).getLocalOffset(); + } + } + SourceRange sourceRange = getRootSourceRange(node); + codeUnit = new BytecodeDSLCodeUnit(toInternedTruffleStringUncached(name), toInternedTruffleStringUncached(qualName), + argumentInfo.argCount, argumentInfo.kwOnlyArgCount, argumentInfo.positionalOnlyArgCount, + flags, orderedTruffleStringArray(names), + orderedTruffleStringArray(varnames), + orderedTruffleStringArray(cellvars), + orderedTruffleStringArray(freevars), + cell2arg, + orderedKeys(constants, new Object[0]), + sourceRange.startLine, + sourceRange.startColumn, + sourceRange.endLine, + sourceRange.endColumn, + classcellIndex, + selfIndex, + yieldFromGenerator != null ? yieldFromGenerator.getLocalIndex() : -1, + instrumentationDataLocal.getLocalIndex(), + maxProfileCEventStackSize, + new BytecodeSupplier(nodes)); + ctx.codeUnits.put(key, codeUnit); + } + rootNode.setMetadata(codeUnit, ctx.errorCallback, ctx.source.isInternal()); return new BytecodeDSLCompilerResult(rootNode, codeUnit); } @@ -477,14 +487,14 @@ static class BytecodeSupplier extends BytecodeDSLCodeUnit.BytecodeSupplier { } @Override - public PBytecodeDSLRootNode createRootNode(PythonContext context, Source source) { + public PBytecodeDSLRootNode createRootNode(PythonLanguage language) { return nodes.getNode(0); } @Override - public byte[] createSerializedBytecode(PythonContext context) { + public byte[] createSerializedBytecode(PythonLanguage language) { try { - BytecodeSerializer serializer = new MarshalModuleBuiltins.PBytecodeDSLSerializer(context); + BytecodeSerializer serializer = new MarshalModuleBuiltins.PBytecodeDSLSerializer(language); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); nodes.serialize(new DataOutputStream(bytes), serializer); return bytes.toByteArray(); @@ -656,16 +666,14 @@ void beginRootNode(SSTNode node, ArgumentsTy args, Builder b) { checkForbiddenArgs(ctx.errorCallback, node.getSourceRange(), args); setUpFrame(args, b); - b.emitTraceOrProfileCall(); - if (node instanceof ClassDef cls) { - if (cls.decoratorList != null && cls.decoratorList.length > 0) { - b.emitTraceLine(cls.decoratorList[0].getSourceRange().startLine); - } else { - b.emitTraceLine(node.getSourceRange().startLine); - } - } else if (node instanceof FunctionDef fn) { - if (fn.decoratorList != null && fn.decoratorList.length > 0) { - b.emitTraceLine(fn.decoratorList[0].getSourceRange().startLine); + if (!scope.isGenerator() && !scope.isCoroutine()) { + b.emitTraceOrProfileCall(); + if (node instanceof ClassDef cls) { + if (cls.decoratorList != null && cls.decoratorList.length > 0) { + b.emitTraceLine(cls.decoratorList[0].getSourceRange().startLine); + } else { + b.emitTraceLine(node.getSourceRange().startLine); + } } } } @@ -783,25 +791,19 @@ boolean beginSourceSection(SSTNode node, Builder b) { return beginSourceSection(node.getSourceRange(), b); } - /** {@link #beginSourceSection(SSTNode, Builder)} */ + /** + * {@link #beginSourceSection(SSTNode, Builder)} + */ boolean beginSourceSection(SourceRange sourceRange, Builder b) { SourceRange oldSourceRange = this.currentLocation; this.currentLocation = sourceRange; - if (ctx.source.hasCharacters()) { - int startOffset = getStartOffset(sourceRange); - int endOffset = getEndOffset(sourceRange); - int length = endOffset - startOffset; - if (length == 0) { - startOffset = 0; - } - b.beginSourceSection(startOffset, length); + beginSourceSectionInner(b, sourceRange); - if (oldSourceRange == null || oldSourceRange.startLine != sourceRange.startLine) { - b.beginTag(StatementTag.class); - b.beginBlock(); - return true; - } + if (oldSourceRange == null || oldSourceRange.startLine != sourceRange.startLine) { + b.beginTag(StatementTag.class); + b.beginBlock(); + return true; } return false; } @@ -813,55 +815,65 @@ boolean beginSourceSection(SourceRange sourceRange, Builder b) { * first line. */ void beginRootSourceSection(SSTNode node, Builder b) { - SourceRange sourceRange; + beginSourceSectionInner(b, getRootSourceRange(node)); + } + + /** + * Decorated class and function roots start at their first decorator for code object metadata and root source sections, + * while still ending at the decorated definition. + */ + private static SourceRange getRootSourceRange(SSTNode node) { if (node instanceof ClassDef cls && cls.decoratorList != null && cls.decoratorList.length > 0) { - sourceRange = cls.decoratorList[0].getSourceRange().withEnd(node.getSourceRange().endLine, node.getSourceRange().endColumn); + return cls.decoratorList[0].getSourceRange().withEnd(node.getSourceRange().endLine, node.getSourceRange().endColumn); + } else if (node instanceof FunctionDef fn && fn.decoratorList != null && fn.decoratorList.length > 0) { + return fn.decoratorList[0].getSourceRange().withEnd(node.getSourceRange().endLine, node.getSourceRange().endColumn); + } else if (node instanceof AsyncFunctionDef fn && fn.decoratorList != null && fn.decoratorList.length > 0) { + return fn.decoratorList[0].getSourceRange().withEnd(node.getSourceRange().endLine, node.getSourceRange().endColumn); } else { - sourceRange = node.getSourceRange(); + return node.getSourceRange(); } + } - if (ctx.source.hasCharacters()) { - int startOffset = getStartOffset(sourceRange); - int endOffset = getEndOffset(sourceRange); - int length = endOffset - startOffset; - if (length == 0) { - startOffset = 0; + private static void beginSourceSectionInner(Builder b, SourceRange sourceRange) { + if (sourceRange.startLine >= 1 && sourceRange != SourceRange.ARTIFICIAL_RANGE) { + if (sourceRange.startColumn >= 0 && sourceRange.endLine >= sourceRange.startLine && sourceRange.endColumn >= 0) { + int startColumn = sourceRange.startColumn + 1; + int endColumn = sourceRange.endColumn > 0 ? sourceRange.endColumn : 1; + if (sourceRange.endLine == sourceRange.startLine && endColumn < startColumn) { + /* + * Truffle doesn't allow source sections with empty or inverted ranges. These are + * rare, but can occur for string constituents of top-level multiline format + * strings and for AST-created code without end-position metadata. + */ + b.beginSourceSection(sourceRange.startLine); + return; + } + b.beginSourceSection(sourceRange.startLine, startColumn, sourceRange.endLine, endColumn); + } else { + b.beginSourceSection(sourceRange.startLine); } - b.beginSourceSection(startOffset, length); + } else { + b.beginSourceSectionUnavailable(); } } void endSourceSection(Builder b, boolean closeTag) { - if (ctx.source.hasCharacters()) { - if (closeTag) { - b.endBlock(); - b.endTag(StatementTag.class); - } - b.endSourceSection(); + if (closeTag) { + b.endBlock(); + b.endTag(StatementTag.class); } + b.endSourceSection(); } void endRootSourceSection(Builder b) { - if (ctx.source.hasCharacters()) { - b.endSourceSection(); - } - } - - int getStartOffset(SourceRange sourceRange) { - return ctx.source.getLineStartOffset(sourceRange.startLine) + sourceRange.startColumn; - } - - int getEndOffset(SourceRange sourceRange) { - return ctx.source.getLineStartOffset(sourceRange.endLine) + sourceRange.endColumn; + b.endSourceSection(); } void beginReturn(Builder b) { b.beginReturn(); - b.beginTraceOrProfileReturn(); } void endReturn(Builder b) { - b.endTraceOrProfileReturn(); b.endReturn(); } @@ -878,7 +890,7 @@ String maybeMangleAndAddName(String name) { @Override public BytecodeDSLCompilerResult visit(ModTy.Module node) { - return compileRootNode("", ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + return compileRootNode("", ArgumentInfo.NO_ARGS, node, b -> { beginRootNode(node, null, b); visitModuleBody(node.body, b, true); endRootNode(b); @@ -887,7 +899,7 @@ public BytecodeDSLCompilerResult visit(ModTy.Module node) { @Override public BytecodeDSLCompilerResult visit(ModTy.Expression node) { - return compileRootNode("", ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + return compileRootNode("", ArgumentInfo.NO_ARGS, node, b -> { beginRootNode(node, null, b); beginReturn(b); new StatementCompiler(b).visitNode(node.body); @@ -898,7 +910,7 @@ public BytecodeDSLCompilerResult visit(ModTy.Expression node) { @Override public BytecodeDSLCompilerResult visit(ModTy.Interactive node) { - return compileRootNode("", ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + return compileRootNode("", ArgumentInfo.NO_ARGS, node, b -> { beginRootNode(node, null, b); visitModuleBody(node.body, b, false); endRootNode(b); @@ -990,8 +1002,8 @@ private static TruffleString getDocstring(StmtTy[] body) { } public BytecodeDSLCompilerResult compileFunctionDef(StmtTy node, String name, ArgumentsTy args, StmtTy[] body) { - return compileRootNode(name, ArgumentInfo.fromArguments(args), node.getSourceRange(), - b -> emitFunctionDefBody(node, args, body, b, getDocstring(body), false)); + return compileRootNode(name, ArgumentInfo.fromArguments(args), + node, b -> emitFunctionDefBody(node, args, body, b, getDocstring(body), false)); } /** @@ -1018,7 +1030,7 @@ private BytecodeDSLCompilerResult compileFunctionTypeParams(BytecodeDSLCodeUnit typeParamsUnitArgs = NO_ARGS; } ArgumentInfo argInfo = ArgumentInfo.fromArguments(typeParamsUnitArgs); - return compileRootNode(name, argInfo, node.getSourceRange(), b -> { + return compileRootNode(name, argInfo, node, b -> { beginRootNode(node, typeParamsUnitArgs, b); StatementCompiler statementCompiler = new StatementCompiler(b); @@ -1062,7 +1074,7 @@ private BytecodeDSLCompilerResult compileFunctionTypeParams(BytecodeDSLCodeUnit private BytecodeDSLCompilerResult compileBoundTypeVar(TypeVar node) { assert node.bound != null; - return compileRootNode(node.name, ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + return compileRootNode(node.name, ArgumentInfo.NO_ARGS, node, b -> { beginRootNode(node, null, b); b.beginReturn(); node.bound.accept(new StatementCompiler(b)); @@ -1073,7 +1085,7 @@ private BytecodeDSLCompilerResult compileBoundTypeVar(TypeVar node) { private BytecodeDSLCompilerResult compileTypeAliasBody(TypeAlias node) { String name = ((ExprTy.Name) node.name).id; - return compileRootNode(name, ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + return compileRootNode(name, ArgumentInfo.NO_ARGS, node, b -> { // Make None the first constant, so the evaluate function can't have a docstring. addObject(constants, PNone.NONE); beginRootNode(node, null, b); @@ -1087,7 +1099,7 @@ private BytecodeDSLCompilerResult compileTypeAliasBody(TypeAlias node) { private BytecodeDSLCompilerResult compileTypeAliasTypeParameters(String name, BytecodeDSLCodeUnit codeUnit, TypeAlias node) { assert this.scopeType == CompilationScope.TypeParams; String typeParamsName = ""; - return compileRootNode(typeParamsName, ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + return compileRootNode(typeParamsName, ArgumentInfo.NO_ARGS, node, b -> { beginRootNode(node, null, b); StatementCompiler statementCompiler = new StatementCompiler(b); statementCompiler.emitBuildTypeAlias(codeUnit, node); @@ -1097,8 +1109,8 @@ private BytecodeDSLCompilerResult compileTypeAliasTypeParameters(String name, By @Override public BytecodeDSLCompilerResult visit(ExprTy.Lambda node) { - return compileRootNode("", ArgumentInfo.fromArguments(node.args), node.getSourceRange(), - b -> emitFunctionDefBody(node, node.args, new SSTNode[]{node.body}, b, null, true)); + return compileRootNode("", ArgumentInfo.fromArguments(node.args), + node, b -> emitFunctionDefBody(node, node.args, new SSTNode[]{node.body}, b, null, true)); } private void emitFunctionDefBody(SSTNode node, ArgumentsTy args, SSTNode[] body, Builder b, Object docstring, boolean isLambda) { @@ -1143,7 +1155,7 @@ private void emitFunctionDefBody(SSTNode node, ArgumentsTy args, SSTNode[] body, } public BytecodeDSLCompilerResult compileClassDefBody(StmtTy.ClassDef node) { - return compileRootNode(node.name, ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + return compileRootNode(node.name, ArgumentInfo.NO_ARGS, node, b -> { beginRootNode(node, null, b); beginStoreLocal("__module__", b); @@ -1215,7 +1227,7 @@ public BytecodeDSLCompilerResult compileClassDefBody(StmtTy.ClassDef node) { public BytecodeDSLCompilerResult compileClassTypeParams(StmtTy.ClassDef node, BytecodeDSLCodeUnit classBody) { assert this.scopeType == CompilationScope.TypeParams; - return compileRootNode(node.name, ArgumentInfo.NO_ARGS, node.getSourceRange(), b -> { + return compileRootNode(node.name, ArgumentInfo.NO_ARGS, node, b -> { beginRootNode(node, null, b); StatementCompiler statementCompiler = new StatementCompiler(b); statementCompiler.emitBuildClass(classBody, node); @@ -1223,7 +1235,7 @@ public BytecodeDSLCompilerResult compileClassTypeParams(StmtTy.ClassDef node, By }); } - private void emitComprehension(ComprehensionTy[] generators, int index, Builder b, + private void emitComprehension(ComprehensionTy[] generators, int index, Builder b, ComprehensionType type, BytecodeLocal collectionLocal, BiConsumer accumulateProducer) { ComprehensionTy comp = generators[index]; @@ -1236,7 +1248,7 @@ private void emitComprehension(ComprehensionTy[] generators, int index, Builder iter = comp.iter; } statementCompiler.emitAsyncFor(iter, comp.target, null, true, index, - (stmtComp, idx) -> emitComprehensionBody(generators, idx, collectionLocal, accumulateProducer, stmtComp)); + (stmtComp, idx) -> emitComprehensionBody(generators, idx, type, collectionLocal, accumulateProducer, stmtComp)); } else { BytecodeLocal localIter = beginTemporaryLocal(b); BytecodeLocal localValue = beginTemporaryLocal(b); @@ -1255,7 +1267,11 @@ private void emitComprehension(ComprehensionTy[] generators, int index, Builder b.beginWhile(); b.beginBlock(); - b.emitTraceLineAtLoopHeader(currentLocation.startLine); + if (type == ComprehensionType.GENEXPR) { + b.emitTraceLineAtLoopHeader(currentLocation.startLine); + } else { + b.emitClearTraceLine(); + } b.beginForIterate(localValue); b.emitLoadLocal(localIter); b.endForIterate(); @@ -1264,7 +1280,7 @@ private void emitComprehension(ComprehensionTy[] generators, int index, Builder b.beginBlock(); comp.target.accept(statementCompiler.new StoreVisitor(() -> b.emitLoadLocal(localValue))); - emitComprehensionBody(generators, index, collectionLocal, accumulateProducer, statementCompiler); + emitComprehensionBody(generators, index, type, collectionLocal, accumulateProducer, statementCompiler); b.endBlock(); b.endWhile(); @@ -1277,7 +1293,7 @@ private void emitComprehension(ComprehensionTy[] generators, int index, Builder } private void emitComprehensionBody(ComprehensionTy[] generators, int index, - BytecodeLocal collectionLocal, BiConsumer accumulateProducer, + ComprehensionType type, BytecodeLocal collectionLocal, BiConsumer accumulateProducer, StatementCompiler statementCompiler) { ComprehensionTy comp = generators[index]; Builder b = statementCompiler.b; @@ -1292,7 +1308,7 @@ private void emitComprehensionBody(ComprehensionTy[] generators, int index, if (index == generators.length - 1) { accumulateProducer.accept(statementCompiler, collectionLocal); } else { - emitComprehension(generators, index + 1, b, collectionLocal, accumulateProducer); + emitComprehension(generators, index + 1, b, type, collectionLocal, accumulateProducer); } if (comp.ifs != null) { @@ -1322,7 +1338,7 @@ private BytecodeDSLCompilerResult buildComprehensionCodeUnit(SSTNode node, Compr if (scope.isCoroutine() && type != ComprehensionType.GENEXPR && scopeType != CompilationScope.AsyncFunction && scopeType != CompilationScope.Comprehension) { throw ctx.errorCallback.onError(ErrorType.Syntax, currentLocation, "asynchronous comprehension outside of an asynchronous function"); } - return compileRootNode(type.name, new ArgumentInfo(1, 0, 0, false, false), node.getSourceRange(), b -> { + return compileRootNode(type.name, new ArgumentInfo(1, 0, 0, false, false), node, b -> { beginRootNode(node, null, b); assert scope.isGenerator() == (type == ComprehensionType.GENEXPR); @@ -1341,7 +1357,7 @@ private BytecodeDSLCompilerResult buildComprehensionCodeUnit(SSTNode node, Compr b.endStoreLocal(); } - emitComprehension(generators, 0, b, collectionLocal, accumulateProducer); + emitComprehension(generators, 0, b, type, collectionLocal, accumulateProducer); beginReturn(b); if (scope.isGenerator()) { @@ -2076,8 +2092,20 @@ private static boolean isAttributeLoad(ExprTy node) { private static final int NUM_ARGS_MAX_FIXED = 4; + private void enterProfileCEventCall() { + profileCEventStackSize++; + maxProfileCEventStackSize = Math.max(maxProfileCEventStackSize, profileCEventStackSize); + } + + private void exitProfileCEventCall() { + assert profileCEventStackSize > 0; + profileCEventStackSize--; + } + private void beginCallNAry(int numArgs) { assert numArgs <= NUM_ARGS_MAX_FIXED; + enterProfileCEventCall(); + b.beginInstrumentCallReturn(); switch (numArgs) { case 0 -> b.beginCallNilaryMethod(); case 1 -> b.beginCallUnaryMethod(); @@ -2096,16 +2124,64 @@ private void endCallNAry(int numArgs) { case 3 -> b.endCallTernaryMethod(); case 4 -> b.endCallQuaternaryMethod(); } + b.endInstrumentCallReturn(); + exitProfileCEventCall(); + } + + private void beginCallNilaryMethod() { + enterProfileCEventCall(); + b.beginInstrumentCallReturn(); + b.beginCallNilaryMethod(); + } + + private void endCallNilaryMethod() { + b.endCallNilaryMethod(); + b.endInstrumentCallReturn(); + exitProfileCEventCall(); + } + + private void beginCallUnaryMethod() { + enterProfileCEventCall(); + b.beginInstrumentCallReturn(); + b.beginCallUnaryMethod(); + } + + private void endCallUnaryMethod() { + b.endCallUnaryMethod(); + b.endInstrumentCallReturn(); + exitProfileCEventCall(); + } + + private void beginCallVarargsMethod() { + enterProfileCEventCall(); + b.beginInstrumentCallReturn(); + b.beginCallVarargsMethod(); + } + + private void endCallVarargsMethod() { + b.endCallVarargsMethod(); + b.endInstrumentCallReturn(); + exitProfileCEventCall(); } private void visitArguments(ExprTy func, ExprTy[] args, int numArgs) { + visitArguments(func, args, numArgs, true); + } + + private void visitArguments(ExprTy func, ExprTy[] args, int numArgs, boolean instrumentCall) { if (numArgs > 0) { for (int i = 0; i < numArgs - 1; i++) { args[i].accept(this); } + if (instrumentCall) { + b.beginInstrumentCall(); + } beginTraceLineChecked(b); args[numArgs - 1].accept(this); endTraceLineChecked(func, b); + if (instrumentCall) { + b.endInstrumentCall(); + } } } @@ -2125,7 +2201,7 @@ private void emitCall(ExprTy func, ExprTy[] args, KeywordTy[] keywords) { // @formatter:off if (useVariadic) { - b.beginCallVarargsMethod(); + beginCallVarargsMethod(); } else { beginCallNAry(numArgs); } @@ -2141,29 +2217,43 @@ private void emitCall(ExprTy func, ExprTy[] args, KeywordTy[] keywords) { function = beginTemporaryLocal(); b.beginBlock(); b.beginStoreLocal(function); + b.beginInstrumentCallable(); emitGetMethod(func, receiver); + b.endInstrumentCallable(); b.endStoreLocal(); b.emitLoadLocal(function); b.endBlock(); } else { + b.beginInstrumentCallable(); emitGetMethod(func, receiver); + b.endInstrumentCallable(); } b.beginCollectToObjectArray(); emitUnstar(() -> loadAndEndTemporaryLocal(receiver), args, null, func); b.endCollectToObjectArray(); + b.beginInstrumentCall(); if (hasKeywords) { emitNonEmptyKeywords(keywordGroups, function); // function local cleared in emitNonEmptyKeywords } else { emitEmptyKeywords(); } + b.endInstrumentCall(); } else { assert len(keywords) == 0; + b.beginInstrumentCallable(); emitGetMethod(func, receiver); - loadAndEndTemporaryLocal(receiver); // callable - visitArguments(func, args, numArgs - 1); + b.endInstrumentCallable(); + if (numArgs == 1) { + b.beginInstrumentCall(); + loadAndEndTemporaryLocal(receiver); // callable + b.endInstrumentCall(); + } else { + loadAndEndTemporaryLocal(receiver); // callable + visitArguments(func, args, numArgs - 1); + } } } else { if (useVariadic) { @@ -2174,34 +2264,60 @@ private void emitCall(ExprTy func, ExprTy[] args, KeywordTy[] keywords) { b.beginStoreLocal(function); func.accept(this); b.endStoreLocal(); + b.beginInstrumentCallable(); b.emitLoadLocal(function); + b.endInstrumentCallable(); b.endBlock(); } else if (hasKeywords) { + b.beginInstrumentCallable(); func.accept(this); + b.endInstrumentCallable(); } else { + b.beginInstrumentCallable(); func.accept(this); + b.endInstrumentCallable(); } b.beginCollectToObjectArray(); emitUnstar(null, args, null, func); b.endCollectToObjectArray(); + b.beginInstrumentCall(); if (hasKeywords) { emitNonEmptyKeywords(keywordGroups, function); // function local cleared in emitNonEmptyKeywords } else { emitEmptyKeywords(); } + b.endInstrumentCall(); } else { assert len(keywords) == 0; + boolean isBreakpoint = func instanceof ExprTy.Name && ((ExprTy.Name) func).id.equals(J_BREAKPOINT); + + if (isBreakpoint) { + b.beginTag(DebuggerTags.AlwaysHalt.class); + } + + if (numArgs == 0) { + b.beginInstrumentCall(); + } + b.beginInstrumentCallable(); func.accept(this); // callable + b.endInstrumentCallable(); + if (numArgs == 0) { + b.endInstrumentCall(); + } visitArguments(func, args, numArgs); + + if (isBreakpoint) { + b.endTag(DebuggerTags.AlwaysHalt.class); + } } } // @formatter:off if (useVariadic) { - b.endCallVarargsMethod(); + endCallVarargsMethod(); } else { endCallNAry(numArgs); } @@ -2785,7 +2901,7 @@ private void emitUnstar(Runnable initialElementsProducer, ExprTy[] args, Runnabl initialElementsProducer.run(); } if (func != null) { - visitArguments(func, args, args.length); + visitArguments(func, args, args.length, false); } else { visitSequence(args); } @@ -3841,10 +3957,7 @@ public Void visit(StmtTy.ClassDef node) { // For type parameters the root node compiler produces intermediate code unit that will // assemble the generic parameters and then call __build_class__ and we just need to // call that code unit - BytecodeLocal[] decoratorsLocals = evaluateDecorators(node.decoratorList); boolean newStatement = beginSourceSection(node, b); - emitTraceLineChecked(node, b); - beginStoreLocal(node.name, b); if (node.decoratorList != null && node.decoratorList.length > 0) { @@ -3852,30 +3965,37 @@ public Void visit(StmtTy.ClassDef node) { beginTraceLineChecked(b); } - beginWrapWithDecorators(decoratorsLocals); + beginWrapWithDecorators(node.decoratorList); + b.beginBlock(); + b.emitTraceLine(node.getSourceRange().startLine); if (node.isGeneric()) { RootNodeCompiler typeParamsCompiler = new RootNodeCompiler(ctx, RootNodeCompiler.this, node.name, node, node.typeParams, futureFeatures); RootNodeCompiler classBodyCompiler = createRootNodeCompilerFor(node, typeParamsCompiler); BytecodeDSLCompilerResult classBody = classBodyCompiler.compileClassDefBody(node); BytecodeDSLCompilerResult typeParamsFun = typeParamsCompiler.compileClassTypeParams(node, classBody.codeUnit()); - b.beginCallNilaryMethod(); + beginCallNilaryMethod(); String typeParamsName = ""; + b.beginInstrumentCall(); + b.beginInstrumentCallable(); emitMakeFunction(typeParamsFun.codeUnit(), node.typeParams, typeParamsName, null, null); - b.endCallNilaryMethod(); + b.endInstrumentCallable(); + b.endInstrumentCall(); + endCallNilaryMethod(); } else { BytecodeDSLCompilerResult classBody = createRootNodeCompilerFor(node).compileClassDefBody(node); emitBuildClass(classBody.codeUnit(), node); } - endWrapWithDecorators(decoratorsLocals, node.decoratorList); + b.endBlock(); + endWrapWithDecorators(node.decoratorList); if (node.decoratorList != null && node.decoratorList.length > 0) { // needs to emit line before return (that will also move the return) endTraceLineChecked(node, b); } - + // we didn't properly update lastTracedLine, force next traceline + lastTracedLine = -1; endStoreLocal(node.name, b); - endTemporaryLocals(decoratorsLocals); endSourceSection(b, newStatement); return null; } @@ -3903,12 +4023,14 @@ private void emitBuildClass(BytecodeDSLCodeUnit body, ClassDef node) { b.endStoreLocal(); } - b.beginCallVarargsMethod(); + beginCallVarargsMethod(); + b.beginInstrumentCallable(); if (hasEmptyKeywords) { b.emitLoadBuildClass(); } else { b.emitLoadLocal(buildClassFunction); } + b.endInstrumentCallable(); Runnable finalElements = null; if (node.isGeneric()) { @@ -3937,14 +4059,16 @@ private void emitBuildClass(BytecodeDSLCodeUnit body, ClassDef node) { b.endCollectToObjectArray(); // keyword args + b.beginInstrumentCall(); if (hasEmptyKeywords) { emitEmptyKeywords(); } else { validateKeywords(node.keywords); emitNonEmptyKeywords(node.keywords, buildClassFunction); } + b.endInstrumentCall(); - b.endCallVarargsMethod(); + endCallVarargsMethod(); b.endBlock(); } @@ -4126,25 +4250,24 @@ public Void visit(StmtTy.FunctionDef node) { } public void emitFunctionDef(StmtTy node, String name, ArgumentsTy args, StmtTy[] body, ExprTy[] decoratorList, ExprTy returns, TypeParamTy[] typeParams) { - BytecodeLocal[] decoratorLocals = evaluateDecorators(decoratorList); // For instrumentation, we want to map this statement only to the declaration line, such // that, e.g., breakpoints inside the body fire only once the body actually executes and // not is declared. There is no simple way to get the exact line width here, so we just // approximate it with name width. boolean newStatement = beginSourceSection(node.getSourceRange().startLineShiftColumn(name.length()), b); - emitTraceLineChecked(node, b); - + // Note: source range of `node` excludes the source range of the decorators beginStoreLocal(name, b); if (decoratorList != null && decoratorList.length > 0) { // needs to emit line before return (that will also move the return) b.beginTraceLineWithArgument(); } - beginWrapWithDecorators(decoratorLocals); + beginWrapWithDecorators(decoratorList); + b.beginTraceLineWithArgument(); boolean isGeneric = typeParams != null && typeParams.length > 0; if (isGeneric) { // The values of default positional and keyword arguments must be passed as - // arguments to the "type parameters" code unit, because we must eveluate them + // arguments to the "type parameters" code unit, because we must evaluate them // already here int argsCount = 0; if (hasDefaultArgs(args)) { @@ -4161,13 +4284,29 @@ public void emitFunctionDef(StmtTy node, String name, ArgumentsTy args, StmtTy[] BytecodeDSLCompilerResult typeParamsFunUnit = typeParamsCompiler.compileFunctionTypeParams(funBodyUnit.codeUnit(), node, name, args, returns, typeParams); String typeParamsName = ""; + if (argsCount == 0) { + b.beginInstrumentCall(); + } + b.beginInstrumentCallable(); emitMakeFunction(typeParamsFunUnit.codeUnit(), typeParams, typeParamsName, null, null); + b.endInstrumentCallable(); + if (argsCount == 0) { + b.endInstrumentCall(); + } if (hasDefaultArgs(args)) { + if (!hasDefaultKwargs(args)) { + b.beginInstrumentCall(); + } emitDefaultArgsArray(args); + if (!hasDefaultKwargs(args)) { + b.endInstrumentCall(); + } } if (hasDefaultKwargs(args)) { + b.beginInstrumentCall(); emitDefaultKwargsArray(args); + b.endInstrumentCall(); } endCallNAry(argsCount); @@ -4175,14 +4314,16 @@ public void emitFunctionDef(StmtTy node, String name, ArgumentsTy args, StmtTy[] BytecodeDSLCompilerResult funBodyCodeUnit = createRootNodeCompilerFor(node).compileFunctionDef(node, name, args, body); emitBuildFunction(funBodyCodeUnit.codeUnit(), node, name, args, decoratorList, returns); } + b.endTraceLineWithArgument(node.getSourceRange().startLine); - endWrapWithDecorators(decoratorLocals, decoratorList); + endWrapWithDecorators(decoratorList); if (decoratorList != null && decoratorList.length > 0) { // needs to emit line before return (that will also move the return) b.endTraceLineWithArgument(node.getSourceRange().startLine); } + // we didn't properly update lastTracedLine, force next traceline + lastTracedLine = -1; endStoreLocal(name, b); - endTemporaryLocals(decoratorLocals); endSourceSection(b, newStatement); } @@ -4252,25 +4393,41 @@ public void endTemporaryLocals(BytecodeLocal[] locals) { * Emits the "opening parentheses" of expression {@code decorator1( decoractor2( ... ( * {value} )) ... )}. */ - public void beginWrapWithDecorators(BytecodeLocal[] locals) { - for (int i = 0; i < locals.length; i++) { - b.beginCallUnaryMethod(); - b.emitLoadLocal(locals[i]); - beginTraceLineChecked(b); + public void beginWrapWithDecorators(ExprTy[] decorators) { + if (decorators == null) { + return; + } + for (int i = 0; i < decorators.length; i++) { + // Attribute the eventual decorator call to the decorator expression, not the def/class line. + beginSourceSectionInner(b, decorators[i].getSourceRange()); + beginCallUnaryMethod(); + // evaluation of the decorator expression + b.beginTraceLineWithArgument(); + b.beginInstrumentCallable(); + decorators[i].accept(this); + b.endInstrumentCallable(); + // trace line for the decorator expression, must be executed before the next + // decorator expression is evaluated and before the function declaration itself + b.endTraceLineWithArgument(decorators[i].getSourceRange().startLine); + + // trace the call to the decorator function (Python 3.12+) + b.beginInstrumentCall(); + b.beginTraceLineWithArgument(); } } - /** - * "Closing parentheses" for {@link #beginWrapWithDecorators(BytecodeLocal[])}. - */ - public void endWrapWithDecorators(BytecodeLocal[] locals, ExprTy[] decorators) { - for (int i = 0; i < locals.length; i++) { + public void endWrapWithDecorators(ExprTy[] decorators) { + if (decorators == null) { + return; + } + for (int i = 0; i < decorators.length; i++) { // we need to trace line in opposite direction -> decorator calls are nested and so - // they will "flip" - // w.r.t. original decorator ordering, but tracings won't, so we need to flip them - // manually - endTraceLineChecked(decorators[locals.length - 1 - i], b); - b.endCallUnaryMethod(); + // they will "flip" w.r.t. original decorator ordering, but tracings won't, so we + // need to flip them manually + b.endTraceLineWithArgument(decorators[decorators.length - 1 - i].getSourceRange().startLine); + b.endInstrumentCall(); + endCallUnaryMethod(); + b.endSourceSection(); } } @@ -4335,8 +4492,9 @@ private void emitMakeFunction(BytecodeDSLCodeUnit codeUnit, Object scopeKey, Str // Register these in the Python constants list. addConstant(codeUnit); + int codeIndex = constants.get(codeUnit); - b.beginMakeFunction(functionName, qualifiedName, new BytecodeDSLCodeUnitAndRoot(codeUnit)); + b.beginMakeFunction(functionName, qualifiedName, codeIndex); if (defaultArgsLocal != null) { assert argsForDefaults == null; @@ -6703,9 +6861,13 @@ public Void visit(TypeAlias node) { BytecodeDSLCompilerResult typeParamsFun = typeParamsCompiler.compileTypeAliasTypeParameters(name, body.codeUnit(), node); String typeParamsName = ""; - b.beginCallNilaryMethod(); + beginCallNilaryMethod(); + b.beginInstrumentCall(); + b.beginInstrumentCallable(); emitMakeFunction(typeParamsFun.codeUnit(), node.typeParams, typeParamsName, null, null); - b.endCallNilaryMethod(); + b.endInstrumentCallable(); + b.endInstrumentCall(); + endCallNilaryMethod(); } else { BytecodeDSLCompilerResult body = createRootNodeCompilerFor(node).compileTypeAliasBody(node); emitBuildTypeAlias(body.codeUnit(), node); 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/nodes/arrow/InvokeArrowReleaseCallbackNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateCheckNode.java similarity index 51% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/InvokeArrowReleaseCallbackNode.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateCheckNode.java index d7986b975d..04943d47e1 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/lib/PyDateCheckNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, 2026, 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 @@ -38,65 +38,53 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.oracle.graal.python.nodes.arrow; +package com.oracle.graal.python.lib; -import com.oracle.graal.python.builtins.objects.cext.common.NativePointer; -import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.runtime.PythonContext; -import com.oracle.graal.python.runtime.arrow.ArrowUtil; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.dsl.Bind; +import com.oracle.graal.python.builtins.modules.datetime.PDate; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; 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.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.nfi.api.SignatureLibrary; -@GenerateCached -@GenerateInline(inlineByDefault = true) +/** Equivalent of CPython's {@code PyDate_Check}. */ @GenerateUncached -public abstract class InvokeArrowReleaseCallbackNode extends PNodeWithContext { +@GenerateInline +@GenerateCached(false) +public abstract class PyDateCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyDateCheckNodeGen.getUncached().execute(null, object); + } - public abstract void execute(Node inliningTarget, long releaseCallback, long baseStructure); + public abstract boolean execute(Node inliningTarget, Object object); - public final void executeCached(long releaseCallback, long baseStructure) { - execute(this, releaseCallback, baseStructure); + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PDate value) { + return true; } @Specialization - static void doIt(Node inliningTarget, long releaseCallback, long baseStructure, - @Bind("getContext(inliningTarget)") PythonContext ctx, - @Cached(value = "createReleaseCallbackSignature($node, ctx)", allowUncached = true) Object callbackSignature, - @CachedLibrary(limit = "1") SignatureLibrary signatureLibrary) { - try { - signatureLibrary.call(callbackSignature, new NativePointer(releaseCallback), baseStructure); - } catch (Exception e) { - throw CompilerDirectives.shouldNotReachHere("Unable to call release callback. Error:", e); - } + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, com.oracle.graal.python.builtins.PythonBuiltinClassType.PDate); } - @NeverDefault - static Object createReleaseCallbackSignature(Node location, PythonContext context) { - return ArrowUtil.createNfiSignature(location, "(UINT64):VOID", context); + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isDate(value); } - @GenerateCached(false) - @GenerateInline - @GenerateUncached - public abstract static class Lazy extends Node { - public final InvokeArrowReleaseCallbackNode get(Node inliningTarget) { - return execute(inliningTarget); - } - - abstract InvokeArrowReleaseCallbackNode execute(Node inliningTarget); - - @Specialization - static InvokeArrowReleaseCallbackNode doIt(@Cached(inline = false) InvokeArrowReleaseCallbackNode node) { - return node; - } + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateTimeCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateTimeCheckNode.java new file mode 100644 index 0000000000..1874d96dc0 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDateTimeCheckNode.java @@ -0,0 +1,90 @@ +/* + * 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.lib; + +import com.oracle.graal.python.builtins.modules.datetime.PDateTime; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +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.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +/** Equivalent of CPython's {@code PyDateTime_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyDateTimeCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyDateTimeCheckNodeGen.getUncached().execute(null, object); + } + + public abstract boolean execute(Node inliningTarget, Object object); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PDateTime value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, com.oracle.graal.python.builtins.PythonBuiltinClassType.PDateTime); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isDate(value) && interop.isTime(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDeltaCheckNode.java similarity index 71% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDeltaCheckNode.java index fbed81342d..9a03066472 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyDeltaCheckNode.java @@ -38,11 +38,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.oracle.graal.python.builtins.modules.datetime; +package com.oracle.graal.python.lib; import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.modules.datetime.PTimeDelta; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; -import com.oracle.graal.python.nodes.object.BuiltinClassProfiles; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; @@ -51,31 +52,30 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; -public class TzInfoNodes { - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class TzInfoCheckNode extends Node { - public abstract boolean execute(Node inliningTarget, Object obj); +/** Equivalent of CPython's {@code PyDelta_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyDeltaCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyDeltaCheckNodeGen.getUncached().execute(null, object); + } - public static boolean executeUncached(Object obj) { - return TzInfoNodesFactory.TzInfoCheckNodeGen.getUncached().execute(null, obj); - } + public abstract boolean execute(Node inliningTarget, Object object); - @Specialization - static boolean doManaged(@SuppressWarnings("unused") PTzInfo value) { - return true; - } + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PTimeDelta value) { + return true; + } - @Specialization - static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile profile) { - return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTzInfo); - } + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, PythonBuiltinClassType.PTimeDelta); + } - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object value) { - return false; - } + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyEnterRecursiveCallNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyEnterRecursiveCallNode.java new file mode 100644 index 0000000000..adda9f8a03 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyEnterRecursiveCallNode.java @@ -0,0 +1,106 @@ +/* + * 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.lib; + +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.RecursionError; + +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.PythonContext.GetThreadStateNode; +import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; +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.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; +import com.oracle.truffle.api.strings.TruffleString; + +/** + * Java-side equivalent of the generic CPython recursion check used around recursive dispatch such + * as {@code PyObject_Repr} and {@code PyObject_Str}; see CPython's {@code _Py_CheckRecursiveCall} + * and {@code _Py_EnterRecursiveCall}. + */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyEnterRecursiveCallNode extends PNodeWithContext { + protected abstract PythonThreadState execute(Node inliningTarget, TruffleString errorMessage, Object formatArg, boolean withFormatArg); + + @Specialization + static PythonThreadState doIt(Node inliningTarget, TruffleString errorMessage, Object formatArg, boolean withFormatArg, + @Cached GetThreadStateNode getThreadStateNode, + @Cached InlinedBranchProfile errorProfile) { + PythonContext context = PythonContext.get(inliningTarget); + PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, context); + if (++threadState.recursionDepth > context.getSysModuleState().getRecursionLimit()) { + threadState.recursionDepth--; + errorProfile.enter(inliningTarget); + if (withFormatArg) { + throw PRaiseNode.raiseStatic(inliningTarget, RecursionError, errorMessage, formatArg); + } + throw PRaiseNode.raiseStatic(inliningTarget, RecursionError, errorMessage); + } + return threadState; + } + + public final PythonThreadState enter(Node inliningTarget, TruffleString errorMessage) { + return execute(inliningTarget, errorMessage, null, false); + } + + public final PythonThreadState enter(Node inliningTarget, TruffleString errorMessage, Object formatArg) { + return execute(inliningTarget, errorMessage, formatArg, true); + } + + public static PythonThreadState enterUncached(Node inliningTarget, TruffleString errorMessage) { + return PyEnterRecursiveCallNodeGen.getUncached().execute(inliningTarget, errorMessage, null, false); + } + + public static PythonThreadState enterUncached(Node inliningTarget, TruffleString errorMessage, Object formatArg) { + return PyEnterRecursiveCallNodeGen.getUncached().execute(inliningTarget, errorMessage, formatArg, true); + } + + public static void leave(PythonThreadState threadState) { + threadState.recursionDepth--; + } +} 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/builtins/objects/cext/common/GetNextVaArgNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyGenFetchStopIterationValue.java similarity index 70% 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/lib/PyGenFetchStopIterationValue.java index c72bb617ce..deddf53367 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/lib/PyGenFetchStopIterationValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, 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 @@ -38,36 +38,31 @@ * 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.lib; -import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction; -import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol; +import com.oracle.graal.python.builtins.objects.exception.BaseExceptionAttrNode; +import com.oracle.graal.python.builtins.objects.exception.PBaseException; +import com.oracle.graal.python.builtins.objects.exception.StopIterationBuiltins; 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; -/** - * Gets the pointer to the outVar at the given index. This is basically an access to the varargs - * like {@code va_arg(*valist, void *)} - */ +@GenerateUncached @GenerateInline @GenerateCached(false) -@GenerateUncached -public abstract class GetNextVaArgNode extends Node { - - public abstract Object execute(Node inliningTarget, Object valist) throws InteropException; +public abstract class PyGenFetchStopIterationValue extends Node { + public abstract Object execute(Node inliningTarget, PBaseException self); - public static Object executeUncached(Object valist) throws InteropException { - return GetNextVaArgNodeGen.getUncached().execute(null, valist); + public static Object executeUncached(PBaseException self) { + return PyGenFetchStopIterationValueNodeGen.getUncached().execute(null, self); } @Specialization - static Object doGeneric(Object valist, - @Cached(inline = false) PCallCapiFunction nextNode) { - return nextNode.call(NativeCAPISymbol.FUN_VA_ARG_POINTER, valist); + static Object doFetch(@SuppressWarnings("unused") Node inliningTarget, PBaseException self, + @Cached BaseExceptionAttrNode attrNode) { + return attrNode.get(self, 0, StopIterationBuiltins.STOP_ITERATION_ATTR_FACTORY); } } 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..b0260fd195 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; @@ -62,9 +61,12 @@ * {@link com.oracle.graal.python.util.OverflowException}. */ @GenerateUncached -@GenerateInline(inlineByDefault = true) +@GenerateInline @GenerateCached(false) public abstract class PyLongAsLongAndOverflowNode extends PNodeWithContext { + public static long executeUncached(Object object) throws OverflowException { + return PyLongAsLongAndOverflowNodeGen.getUncached().execute(null, null, object); + } public abstract long execute(Frame frame, Node inliningTarget, Object object) throws OverflowException; @@ -91,11 +93,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 +136,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/PyNumberAddNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberAddNode.java index 9bd52f3561..021ed24fd4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberAddNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberAddNode.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.object.GetClassNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.bytecode.StoreBytecodeIndex; import com.oracle.truffle.api.dsl.Bind; @@ -75,7 +76,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberAddNode extends PyNumberAddFastPathsBase { @Specialization(guards = {"isBuiltinList(left)", "isBuiltinList(right)"}) @@ -103,6 +104,7 @@ public static TruffleString doIt(TruffleString left, TruffleString right, } @Fallback + @InliningCutoff @StoreBytecodeIndex public static Object doIt(VirtualFrame frame, Object v, Object w, @Bind Node inliningTarget, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberAndNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberAndNode.java index 2f16047891..a371f7f559 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberAndNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberAndNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) public abstract class PyNumberAndNode extends PyNumberAndFastPathsBase { @Fallback 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/PyNumberFloorDivideNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberFloorDivideNode.java index e88c0cf4bc..89abf5aa77 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberFloorDivideNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberFloorDivideNode.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 @@ -42,6 +42,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.ReversibleSlot; import com.oracle.graal.python.lib.fastpath.PyNumberFloorDivideFastPathsBase; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.bytecode.StoreBytecodeIndex; import com.oracle.truffle.api.dsl.Bind; @@ -55,11 +56,12 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) public abstract class PyNumberFloorDivideNode extends PyNumberFloorDivideFastPathsBase { @Fallback @StoreBytecodeIndex + @InliningCutoff public static Object doIt(VirtualFrame frame, Object v, Object w, @Bind Node inliningTarget, @Cached CallBinaryOpNode callBinaryOpNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceAddNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceAddNode.java index 8790d606ac..f0c40ef463 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceAddNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceAddNode.java @@ -67,7 +67,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceAddNode extends PyNumberAddFastPathsBase { @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceAndNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceAndNode.java index 4b0735d476..c5e6e63fb7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceAndNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceAndNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceAndNode extends PyNumberAndFastPathsBase { @Fallback @InliningCutoff diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceFloorDivideNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceFloorDivideNode.java index 24035a4e2a..d9c4f0e734 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceFloorDivideNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceFloorDivideNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceFloorDivideNode extends PyNumberFloorDivideFastPathsBase { @Fallback @InliningCutoff diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceLshiftNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceLshiftNode.java index 12629dffa5..ca6f04d0ad 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceLshiftNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceLshiftNode.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 @@ -41,7 +41,8 @@ package com.oracle.graal.python.lib; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.InplaceSlot; -import com.oracle.graal.python.nodes.expression.BinaryOpNode; +import com.oracle.graal.python.lib.fastpath.PyNumberLshiftFastPathsBase; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.bytecode.StoreBytecodeIndex; import com.oracle.truffle.api.dsl.Bind; @@ -53,13 +54,13 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -// TODO: should inherit from PyNumberLshiftFastPathsBase, blocked by GR-64005 @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) -public abstract class PyNumberInPlaceLshiftNode extends BinaryOpNode { - @Specialization // (replaces = {"doII", "doLL"}) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) +public abstract class PyNumberInPlaceLshiftNode extends PyNumberLshiftFastPathsBase { + @Specialization(replaces = {"doII", "doLL"}) @StoreBytecodeIndex + @InliningCutoff public static Object doIt(VirtualFrame frame, Object v, Object w, @Bind Node inliningTarget, @Cached CallBinaryIOpNode callBinaryOpNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceMatrixMultiplyNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceMatrixMultiplyNode.java index 640e2129c9..3a13a426c9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceMatrixMultiplyNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceMatrixMultiplyNode.java @@ -55,7 +55,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceMatrixMultiplyNode extends BinaryOpNode { @Specialization @StoreBytecodeIndex diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceMultiplyNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceMultiplyNode.java index 095a212b9d..0241b6bbc9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceMultiplyNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceMultiplyNode.java @@ -66,7 +66,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceMultiplyNode extends PyNumberMultiplyFastPathsBase { @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceOrNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceOrNode.java index 2de86fd4a4..c6cc4ae458 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceOrNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceOrNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceOrNode extends PyNumberOrFastPathsBase { @Fallback @InliningCutoff diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceRemainderNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceRemainderNode.java index 58dd885f1f..33367781ac 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceRemainderNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceRemainderNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceRemainderNode extends PyNumberRemainderFastPathsBase { @Fallback @InliningCutoff diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceRshiftNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceRshiftNode.java index 53901e7a77..be4b3e1432 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceRshiftNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceRshiftNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceRshiftNode extends PyNumberRshiftFastPathsBase { @Fallback @InliningCutoff diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceSubtractNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceSubtractNode.java index 934818d80c..60c482f427 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceSubtractNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceSubtractNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceSubtractNode extends PyNumberSubtractFastPathsBase { @Fallback @InliningCutoff diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceTrueDivideNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceTrueDivideNode.java index 4fdf54c81d..4a6ff63cbf 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceTrueDivideNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceTrueDivideNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceTrueDivideNode extends PyNumberTrueDivideFastPathsBase { @Fallback @InliningCutoff diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceXorNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceXorNode.java index b7828a3c5b..bc7c38c959 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceXorNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInPlaceXorNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInPlaceXorNode extends PyNumberXorFastPathsBase { @Fallback @InliningCutoff diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInvertNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInvertNode.java index de0b4dd387..b1443855e4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInvertNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberInvertNode.java @@ -63,7 +63,7 @@ @GenerateUncached @GenerateInline(false) -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberInvertNode extends UnaryOpNode { @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberLongNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberLongNode.java index 831553f7ed..fe4e005d79 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberLongNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberLongNode.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,7 +44,6 @@ import static com.oracle.graal.python.nodes.SpecialMethodNames.J___INT__; import static com.oracle.graal.python.nodes.SpecialMethodNames.J___TRUNC__; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___TRUNC__; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins; @@ -241,8 +240,7 @@ public abstract static class LongFromBufferNode extends Node { @InliningCutoff static Object doGeneric(VirtualFrame frame, Object object, int base, @Bind Node inliningTarget, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode, @Cached PyLongFromUnicodeObject fromString, @Cached(value = "createFor($node)") InteropCallData callData, @CachedLibrary(limit = "3") PythonBufferAcquireLibrary acquireLib, @@ -256,8 +254,7 @@ static Object doGeneric(VirtualFrame frame, Object object, int base, try { byte[] bytes = bufferLib.getInternalOrCopiedByteArray(buffer); int len = bufferLib.getBufferLength(buffer); - TruffleString string = fromByteArrayNode.execute(bytes, 0, len, TruffleString.Encoding.US_ASCII, false); - string = switchEncodingNode.execute(string, TS_ENCODING); + TruffleString string = fromByteArrayNode.execute(bytes, 0, len, TruffleString.CompactionLevel.S1, false); return fromString.execute(inliningTarget, string, base, bytes, len); } finally { bufferLib.release(buffer); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberLshiftNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberLshiftNode.java index 74a4e3733a..f2ccdf2dff 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberLshiftNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberLshiftNode.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 @@ -41,7 +41,8 @@ package com.oracle.graal.python.lib; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.ReversibleSlot; -import com.oracle.graal.python.nodes.expression.BinaryOpNode; +import com.oracle.graal.python.lib.fastpath.PyNumberLshiftFastPathsBase; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.bytecode.StoreBytecodeIndex; import com.oracle.truffle.api.dsl.Bind; @@ -53,14 +54,14 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -// TODO: should inherit from PyNumberLshiftFastPathsBase, blocked by GR-64005 @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) -public abstract class PyNumberLshiftNode extends BinaryOpNode { +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) +public abstract class PyNumberLshiftNode extends PyNumberLshiftFastPathsBase { - @Specialization // (replaces = {"doII", "doLL"}) + @Specialization(replaces = {"doII", "doLL"}) @StoreBytecodeIndex + @InliningCutoff public static Object doIt(VirtualFrame frame, Object v, Object w, @Bind Node inliningTarget, @Cached CallBinaryOpNode callBinaryOpNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberMatrixMultiplyNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberMatrixMultiplyNode.java index 6991babf95..87755166ee 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberMatrixMultiplyNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberMatrixMultiplyNode.java @@ -54,7 +54,7 @@ import com.oracle.truffle.api.nodes.Node; @GenerateInline(false) -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) @GenerateUncached public abstract class PyNumberMatrixMultiplyNode extends BinaryOpNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberMultiplyNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberMultiplyNode.java index 938d8e76a2..418474c4d4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberMultiplyNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberMultiplyNode.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 @@ -49,6 +49,7 @@ import com.oracle.graal.python.lib.fastpath.PyNumberMultiplyFastPathsBase; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.bytecode.StoreBytecodeIndex; import com.oracle.truffle.api.dsl.Bind; @@ -64,11 +65,12 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberMultiplyNode extends PyNumberMultiplyFastPathsBase { @Fallback @StoreBytecodeIndex + @InliningCutoff public static Object doIt(VirtualFrame frame, Object v, Object w, @Bind Node inliningTarget, @Exclusive @Cached GetClassNode getVClass, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberNegativeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberNegativeNode.java index 32b7a70eab..daf6f52747 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberNegativeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberNegativeNode.java @@ -63,7 +63,7 @@ @GenerateUncached @GenerateInline(false) -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberNegativeNode extends UnaryOpNode { public static final int INT_MIN_VALUE = Integer.MIN_VALUE; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberOrNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberOrNode.java index 1c8e587a6d..89d251de16 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberOrNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberOrNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberOrNode extends PyNumberOrFastPathsBase { @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberPositiveNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberPositiveNode.java index d4e12cb827..f1927d9f12 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberPositiveNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberPositiveNode.java @@ -63,7 +63,7 @@ @GenerateUncached @GenerateInline(false) -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyNumberPositiveNode extends UnaryOpNode { @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberRemainderNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberRemainderNode.java index bb967280fd..ec475a2a81 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberRemainderNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberRemainderNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) public abstract class PyNumberRemainderNode extends PyNumberRemainderFastPathsBase { @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberRshiftNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberRshiftNode.java index 3b4cb93e20..7b57b7089b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberRshiftNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberRshiftNode.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 @@ -69,11 +69,12 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) public abstract class PyNumberRshiftNode extends PyNumberRshiftFastPathsBase { @Fallback @StoreBytecodeIndex + @InliningCutoff public static Object doIt(VirtualFrame frame, Object v, Object w, @Bind Node inliningTarget, @Cached GetClassNode getVClass, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberSubtractNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberSubtractNode.java index 0806d678f2..9f9d04f9f5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberSubtractNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberSubtractNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) public abstract class PyNumberSubtractNode extends PyNumberSubtractFastPathsBase { @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberTrueDivideNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberTrueDivideNode.java index ba5a7e6a81..d29d1d5f06 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberTrueDivideNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberTrueDivideNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) public abstract class PyNumberTrueDivideNode extends PyNumberTrueDivideFastPathsBase { @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberXorNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberXorNode.java index ad9a46a99e..bd7cac2e4a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberXorNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberXorNode.java @@ -56,7 +56,7 @@ @GenerateInline(false) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) public abstract class PyNumberXorNode extends PyNumberXorFastPathsBase { @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyOSFSPathNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyOSFSPathNode.java index dd49945815..ff503a62f8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyOSFSPathNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyOSFSPathNode.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,7 +42,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___FSPATH__; -import static com.oracle.graal.python.nodes.truffle.TruffleStringMigrationHelpers.isJavaString; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.bytes.PBytes; @@ -54,6 +53,7 @@ import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode; import com.oracle.graal.python.nodes.object.GetClassNode; 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; @@ -89,11 +89,25 @@ static Object doBytes(PBytes object) { return object; } + @Specialization(guards = "unicodeCheckNode.execute(inliningTarget, object)", limit = "1") + static Object doStringSubtype(Node inliningTarget, Object object, + @Exclusive @Cached PyUnicodeCheckNode unicodeCheckNode) { + return object; + } + + @Specialization(guards = "bytesCheckNode.execute(inliningTarget, object)", limit = "1") + static Object doBytesSubtype(Node inliningTarget, Object object, + @Exclusive @Cached PyBytesCheckNode bytesCheckNode) { + return object; + } + @Fallback static Object callFspath(VirtualFrame frame, Node inliningTarget, Object object, @Cached GetClassNode getClassNode, @Cached LookupSpecialMethodNode.Dynamic lookupFSPath, @Cached(inline = false) CallUnaryMethodNode callFSPath, + @Exclusive @Cached PyUnicodeCheckNode unicodeCheckNode, + @Exclusive @Cached PyBytesCheckNode bytesCheckNode, @Cached PRaiseNode raiseNode) { Object type = getClassNode.execute(inliningTarget, object); Object fspathMethod = lookupFSPath.execute(frame, inliningTarget, type, T___FSPATH__, object); @@ -101,8 +115,7 @@ static Object callFspath(VirtualFrame frame, Node inliningTarget, Object object, throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.EXPECTED_STR_BYTE_OSPATHLIKE_OBJ, object); } Object result = callFSPath.executeObject(frame, fspathMethod, object); - assert !isJavaString(result); - if (result instanceof TruffleString || result instanceof PString || result instanceof PBytes) { + if (unicodeCheckNode.execute(inliningTarget, result) || bytesCheckNode.execute(inliningTarget, result)) { return result; } throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.EXPECTED_FSPATH_TO_RETURN_STR_OR_BYTES, object, result); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiAsObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiAsObjectNode.java new file mode 100644 index 0000000000..bbeb7b6905 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiAsObjectNode.java @@ -0,0 +1,115 @@ +/* + * 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 + * + * 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.lib; + +import static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.unicodeNonAsciiEscape; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; + +import com.oracle.graal.python.nodes.PNodeWithContext; +import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; +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.NeverDefault; +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.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; +import com.oracle.truffle.api.strings.TruffleStringIterator; + +/** + * Equivalent of CPython's PyObject_ASCII. + */ +@GenerateUncached +@GenerateInline(inlineByDefault = true) +@GenerateCached +public abstract class PyObjectAsciiAsObjectNode extends PNodeWithContext { + public static Object executeUncached(Object object) { + return PyObjectAsciiAsObjectNodeGen.getUncached().execute(null, null, object); + } + + public final Object executeCached(Frame frame, Object object) { + return execute(frame, this, object); + } + + public abstract Object execute(Frame frame, Node inliningTarget, Object object); + + @Specialization + public static Object ascii(VirtualFrame frame, Node inliningTarget, Object obj, + @Cached PyObjectReprAsObjectNode reprNode, + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached TruffleString.GetCodeRangeNode getCodeRangeNode, + @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, + @Cached TruffleStringIterator.NextNode nextNode, + @Cached TruffleString.CodePointLengthNode codePointLengthNode, + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { + Object reprObj = reprNode.execute(frame, inliningTarget, obj); + TruffleString repr = castToTruffleStringNode.castKnownString(inliningTarget, reprObj); + if (getCodeRangeNode.execute(repr, TS_ENCODING) == TruffleString.CodeRange.ASCII) { + return reprObj; + } + return convertToAscii(repr, createCodePointIteratorNode, nextNode, codePointLengthNode, fromByteArrayNode); + } + + public static TruffleString convertToAscii(TruffleString repr, TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, TruffleStringIterator.NextNode nextNode, + TruffleString.CodePointLengthNode codePointLengthNode, TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { + // TODO GR-37220: rewrite using TruffleStringBuilder? + byte[] bytes = new byte[codePointLengthNode.execute(repr, TS_ENCODING) * 10]; + TruffleStringIterator it = createCodePointIteratorNode.execute(repr, TS_ENCODING); + int j = 0; + while (it.hasNext()) { + int ch = nextNode.execute(it, TS_ENCODING); + j = unicodeNonAsciiEscape(ch, j, bytes); + } + return fromByteArrayNode.execute(bytes, 0, j, TruffleString.CompactionLevel.S1, true); + } + + @NeverDefault + public static PyObjectAsciiAsObjectNode create() { + return PyObjectAsciiAsObjectNodeGen.create(); + } + + public static PyObjectAsciiAsObjectNode getUncached() { + return PyObjectAsciiAsObjectNodeGen.getUncached(); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiAsTruffleStringNode.java similarity index 75% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiNode.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiAsTruffleStringNode.java index f1d4063e88..75e9f51df4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectAsciiAsTruffleStringNode.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 static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.unicodeNonAsciiEscape; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import com.oracle.graal.python.nodes.PNodeWithContext; @@ -54,7 +53,6 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; -import com.oracle.truffle.api.strings.TruffleString.Encoding; import com.oracle.truffle.api.strings.TruffleStringIterator; /** @@ -63,9 +61,9 @@ @GenerateUncached @GenerateInline(inlineByDefault = true) @GenerateCached -public abstract class PyObjectAsciiNode extends PNodeWithContext { +public abstract class PyObjectAsciiAsTruffleStringNode extends PNodeWithContext { public static TruffleString executeUncached(Object object) { - return PyObjectAsciiNodeGen.getUncached().execute(null, null, object); + return PyObjectAsciiAsTruffleStringNodeGen.getUncached().execute(null, null, object); } public final TruffleString executeCached(Frame frame, Object object) { @@ -81,29 +79,20 @@ public static TruffleString ascii(VirtualFrame frame, Node inliningTarget, Objec @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, @Cached TruffleStringIterator.NextNode nextNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { - // TODO GR-37220: rewrite using TruffleStringBuilder? + @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromByteArrayNode) { TruffleString repr = reprNode.execute(frame, inliningTarget, obj); if (getCodeRangeNode.execute(repr, TS_ENCODING) == TruffleString.CodeRange.ASCII) { return repr; } - byte[] bytes = new byte[codePointLengthNode.execute(repr, TS_ENCODING) * 10]; - TruffleStringIterator it = createCodePointIteratorNode.execute(repr, TS_ENCODING); - int j = 0; - while (it.hasNext()) { - int ch = nextNode.execute(it, TS_ENCODING); - j = unicodeNonAsciiEscape(ch, j, bytes); - } - return switchEncodingNode.execute(fromByteArrayNode.execute(bytes, 0, j, Encoding.US_ASCII, true), TS_ENCODING); + return PyObjectAsciiAsObjectNode.convertToAscii(repr, createCodePointIteratorNode, nextNode, codePointLengthNode, fromByteArrayNode); } @NeverDefault - public static PyObjectAsciiNode create() { - return PyObjectAsciiNodeGen.create(); + public static PyObjectAsciiAsTruffleStringNode create() { + return PyObjectAsciiAsTruffleStringNodeGen.create(); } - public static PyObjectAsciiNode getUncached() { - return PyObjectAsciiNodeGen.getUncached(); + public static PyObjectAsciiAsTruffleStringNode getUncached() { + return PyObjectAsciiAsTruffleStringNodeGen.getUncached(); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectDir.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectDir.java index 93f75625cd..be5bd9f315 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectDir.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectDir.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,7 +42,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; -import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.list.ListBuiltins; import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.nodes.ErrorMessages; @@ -50,6 +49,7 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; @@ -72,8 +72,10 @@ static PList dir(VirtualFrame frame, Node inliningTarget, Object object, @Cached(inline = false) ListNodes.ConstructListNode constructListNode, @Cached(value = "create(T___DIR__)", inline = false) LookupAndCallUnaryNode callDir, @Cached PRaiseNode raiseNode) { - Object result = callDir.executeObject(frame, object); - if (result == PNone.NO_VALUE) { + Object result; + try { + result = callDir.executeObject(frame, object); + } catch (SpecialMethodNotFound e) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.OBJ_DOES_NOT_PROVIDE_DIR); } PList list = constructListNode.execute(frame, result); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectFormat.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectFormat.java index 5a398d5249..fcdfaec902 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectFormat.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectFormat.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 @@ -53,14 +53,15 @@ import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.truffle.api.bytecode.OperationProxy; 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; 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.Specialization; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; @@ -73,23 +74,32 @@ * not accept native {@code NULL} as {@code formatSpec}. Convert it to an empty string to get * equivalent behavior. */ -@GenerateInline +@GenerateInline(false) @GenerateUncached +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = true) public abstract class PyObjectFormat extends PNodeWithContext { - public final Object executeNonInlined(VirtualFrame frame, Object obj, Object formatSpec) { - return execute(frame, null, obj, formatSpec); + + public abstract Object execute(Frame frame, Object obj, Object formatSpec); + + @NeverDefault + public static PyObjectFormat create() { + return PyObjectFormatNodeGen.create(); } - public abstract Object execute(VirtualFrame frame, Node node, Object obj, Object formatSpec); + public static PyObjectFormat getUncached() { + return PyObjectFormatNodeGen.getUncached(); + } - @Specialization - static Object doNone(VirtualFrame frame, Node inliningTarget, Object obj, PNone formatSpec, + @Specialization(guards = "isNoValue(formatSpec)") + public static Object doNone(VirtualFrame frame, Object obj, PNone formatSpec, + @Bind Node inliningTarget, @Shared("impl") @Cached PyObjectFormatStr formatStr) { return formatStr.execute(frame, inliningTarget, obj, T_EMPTY_STRING); } @Fallback - static Object doOthers(VirtualFrame frame, Node inliningTarget, Object obj, Object formatSpec, + public static Object doOthers(VirtualFrame frame, Object obj, Object formatSpec, + @Bind Node inliningTarget, @Shared("impl") @Cached PyObjectFormatStr formatStr) { return formatStr.execute(frame, inliningTarget, obj, formatSpec); } @@ -100,31 +110,21 @@ static Object doOthers(VirtualFrame frame, Node inliningTarget, Object obj, Obje public abstract static class PyObjectFormatStr extends PNodeWithContext { public abstract Object execute(Frame frame, Node inliningTarget, Object obj, Object formatSpec); - static boolean isEmptyString(Object formatSpec) { - // to keep the fast-path optimization guard simple, we ignore empty PStrings - return (formatSpec instanceof TruffleString && ((TruffleString) formatSpec).isEmpty()); - } - - @Specialization(guards = {"isString(obj)", "isEmptyString(formatSpec)"}) - static Object doString(Object obj, Object formatSpec) { - return obj; - } - - @Specialization(guards = {"isEmptyString(formatSpec)"}) - static Object doLong(long obj, Object formatSpec) { + // to keep the fast-path optimization guard simple, we ignore PStrings + @Specialization(guards = {"formatSpec.isEmpty()"}) + static Object doString(TruffleString obj, TruffleString formatSpec) { return obj; } - // Note: PRaiseNode is @Exclusive to workaround a bug in DSL @Specialization(guards = "isString(formatSpec)") static Object doGeneric(VirtualFrame frame, Node inliningTarget, Object obj, Object formatSpec, @Cached GetClassNode getClassNode, @Cached LookupSpecialMethodNode.Dynamic lookupFormat, @Cached CallBinaryMethodNode callFormat, - @Exclusive @Cached PRaiseNode raiseNode) { + @Cached PRaiseNode raiseNode) { Object formatMethod = lookupFormat.execute(frame, inliningTarget, getClassNode.execute(inliningTarget, obj), T___FORMAT__, obj); if (formatMethod != PNone.NO_VALUE) { - Object res = callFormat.executeObject(frame, obj, formatSpec); + Object res = callFormat.executeObject(frame, formatMethod, obj, formatSpec); if (!PGuards.isString(res)) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.S_MUST_RETURN_S_NOT_P, T___FORMAT__, "str", res); } @@ -134,7 +134,6 @@ static Object doGeneric(VirtualFrame frame, Node inliningTarget, Object obj, Obj } } - // Note: PRaiseNode is @Exclusive to workaround a bug in DSL @Fallback static Object doNonStringFormat(Object obj, Object formatSpec, @Bind Node inliningTarget) { 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..91970605f4 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.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.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, + PythonToNativeInternalNode.executeUncached(klass, false)); if (res < 0) { throw raiseSystemError(inliningTarget, klass); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsInstanceNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsInstanceNode.java new file mode 100644 index 0000000000..84b729f2d8 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsInstanceNode.java @@ -0,0 +1,119 @@ +/* + * 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.lib; + +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___INSTANCECHECK__; + +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; +import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; +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.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; + +/** + * Equivalent of CPython's {@code PyObject_IsInstance}. Implements tuple recursion for + * {@code classinfo} and the {@code __instancecheck__} fallback protocol. + */ +@GenerateInline(false) +@GenerateUncached +@ImportStatic(PGuards.class) +public abstract class PyObjectIsInstanceNode extends PyObjectRecursiveBinaryCheckNode { + @Override + PyObjectRecursiveBinaryCheckNode createRecursive() { + return create(); + } + + @Override + PyObjectRecursiveBinaryCheckNode getUncachedRecursive() { + return getUncached(); + } + + @NeverDefault + public static PyObjectIsInstanceNode create() { + return PyObjectIsInstanceNodeGen.create(); + } + + public static PyObjectIsInstanceNode getUncached() { + return PyObjectIsInstanceNodeGen.getUncached(); + } + + @Specialization(guards = "!tupleCheck.execute(inliningTarget, cls)", insertBefore = "doRecursiveWithNode", limit = "1") + static boolean isInstance(VirtualFrame frame, Object instance, Object cls, @SuppressWarnings("unused") int depth, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @Cached GetClassNode getClsClassNode, + @Cached IsBuiltinClassExactProfile classProfile, + @Cached GetClassNode getInstanceClassNode, + @Cached TypeNodes.IsSameTypeNode isSameTypeNode, + @Cached LookupSpecialMethodNode.Dynamic instanceCheckLookup, + @Cached CallBinaryMethodNode callInstanceCheck, + @Cached PyObjectIsTrueNode isTrueNode, + @Cached TypeNodes.GenericInstanceCheckNode genericInstanceCheckNode, + @Cached InlinedBranchProfile noInstanceCheckProfile) { + if (isSameTypeNode.execute(inliningTarget, getInstanceClassNode.execute(inliningTarget, instance), cls)) { + // Exact match, don't call __instancecheck__ + return true; + } + if (classProfile.profileClass(inliningTarget, getClsClassNode.execute(inliningTarget, cls), PythonBuiltinClassType.PythonClass)) { + // Avoid the lookup and call overhead when we know we're calling type.__instancecheck__. + return genericInstanceCheckNode.execute(frame, inliningTarget, instance, cls); + } + Object method = instanceCheckLookup.execute(frame, inliningTarget, getClsClassNode.execute(inliningTarget, cls), T___INSTANCECHECK__, cls); + if (PGuards.isNoValue(method)) { + noInstanceCheckProfile.enter(inliningTarget); + return genericInstanceCheckNode.execute(frame, inliningTarget, instance, cls); + } + Object result = callInstanceCheck.executeObject(frame, method, cls, instance); + return isTrueNode.execute(frame, result); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsNotTrueNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsNotTrueNode.java index dfe83aa970..c78b919a8c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsNotTrueNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsNotTrueNode.java @@ -55,6 +55,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; 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.Specialization; import com.oracle.truffle.api.frame.Frame; @@ -68,7 +69,8 @@ * {@link PyObjectIsNotTrueNode}. */ @GenerateInline(false) -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@GenerateUncached +@OperationProxy.Proxyable(storeBytecodeIndex = false, allowUncached = true) public abstract class PyObjectIsNotTrueNode extends PNodeWithContext { public abstract boolean execute(Frame frame, Object object); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsSubclassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsSubclassNode.java new file mode 100644 index 0000000000..4ba85c6c47 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsSubclassNode.java @@ -0,0 +1,113 @@ +/* + * 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.lib; + +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SUBCLASSCHECK__; + +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.objects.type.TypeNodes; +import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; +import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; +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.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; + +/** + * Equivalent of CPython's {@code PyObject_IsSubclass}. Implements tuple recursion for + * {@code classinfo} and the {@code __subclasscheck__} fallback protocol. + */ +@GenerateInline(false) +@GenerateUncached +@ImportStatic(PGuards.class) +public abstract class PyObjectIsSubclassNode extends PyObjectRecursiveBinaryCheckNode { + @Override + PyObjectRecursiveBinaryCheckNode createRecursive() { + return create(); + } + + @Override + PyObjectRecursiveBinaryCheckNode getUncachedRecursive() { + return getUncached(); + } + + @NeverDefault + public static PyObjectIsSubclassNode create() { + return PyObjectIsSubclassNodeGen.create(); + } + + public static PyObjectIsSubclassNode getUncached() { + return PyObjectIsSubclassNodeGen.getUncached(); + } + + @Specialization(guards = "!tupleCheck.execute(inliningTarget, cls)", insertBefore = "doRecursiveWithNode", limit = "1") + static boolean isSubclass(VirtualFrame frame, Object derived, Object cls, @SuppressWarnings("unused") int depth, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @Cached GetClassNode getClsClassNode, + @Cached IsBuiltinClassExactProfile classProfile, + @Cached LookupSpecialMethodNode.Dynamic subclassCheckLookup, + @Cached CallBinaryMethodNode callSubclassCheck, + @Cached PyObjectIsTrueNode isTrueNode, + @Cached TypeNodes.GenericSubclassCheckNode genericSubclassCheckNode, + @Cached InlinedBranchProfile noInstanceCheckProfile) { + if (classProfile.profileClass(inliningTarget, getClsClassNode.execute(inliningTarget, cls), PythonBuiltinClassType.PythonClass)) { + // Avoid the lookup and call overhead when we know we're calling type.__subclasscheck__. + return genericSubclassCheckNode.execute(frame, inliningTarget, derived, cls); + } + Object method = subclassCheckLookup.execute(frame, inliningTarget, getClsClassNode.execute(inliningTarget, cls), T___SUBCLASSCHECK__, cls); + if (PGuards.isNoValue(method)) { + noInstanceCheckProfile.enter(inliningTarget); + return genericSubclassCheckNode.execute(frame, inliningTarget, derived, cls); + } + Object result = callSubclassCheck.executeObject(frame, method, cls, derived); + return isTrueNode.execute(frame, result); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsTrueNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsTrueNode.java index 12496534f6..2737745745 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsTrueNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectIsTrueNode.java @@ -78,7 +78,7 @@ @GenerateUncached @GenerateInline(false) @GenerateCached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class PyObjectIsTrueNode extends PNodeWithContext { public abstract boolean execute(Frame frame, Object object); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRecursiveBinaryCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRecursiveBinaryCheckNode.java new file mode 100644 index 0000000000..6d182dfd60 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRecursiveBinaryCheckNode.java @@ -0,0 +1,149 @@ +/* + * 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.lib; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; +import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.PNodeWithContext; +import com.oracle.graal.python.nodes.builtins.TupleNodes; +import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; +import com.oracle.graal.python.runtime.PythonOptions; +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.GenerateCached; +import com.oracle.truffle.api.dsl.GenerateInline; +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.Specialization; +import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.UnadoptableNode; + +/** + * Base for recursive binary checks like {@code isinstance} and {@code issubclass}. It implements + * the recursive iteration of tuple {@code classinfo}; subclasses provide the scalar check. + */ +@ImportStatic({PythonOptions.class, PGuards.class}) +@GenerateInline(false) +@GenerateCached(false) +@SuppressWarnings("truffle-neverdefault") +abstract class PyObjectRecursiveBinaryCheckNode extends PNodeWithContext { + public final boolean execute(Frame frame, Object arg, Object classinfo) { + return executeInternal(frame, arg, classinfo, 0); + } + + protected abstract boolean executeInternal(Frame frame, Object arg, Object classinfo, int depth); + + @NeverDefault + abstract PyObjectRecursiveBinaryCheckNode createRecursive(); + + abstract PyObjectRecursiveBinaryCheckNode getUncachedRecursive(); + + @Specialization(guards = {"depth < getNodeRecursionLimit(language)", "tupleCheck.execute(inliningTarget, clsTuple)"}, limit = "1", excludeForUncached = true) + static boolean doRecursiveWithNode(VirtualFrame frame, Object arg, Object clsTuple, int depth, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Bind PythonLanguage language, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Cached SequenceStorageNodes.ToArrayNode toArrayNode, + @Cached("createRecursive()") PyObjectRecursiveBinaryCheckNode recursiveNode) { + return loopRecursive(frame, arg, clsTuple, inliningTarget, getTupleStorage, toArrayNode, recursiveNode, depth + 1); + } + + @SuppressWarnings("truffle-static-method") + @Specialization(guards = {"depth >= getNodeRecursionLimit(language)", "tupleCheck.execute(inliningTarget, clsTuple)"}, limit = "1", excludeForUncached = true) + boolean doRecursiveTransition(VirtualFrame frame, Object arg, Object clsTuple, @SuppressWarnings("unused") int depth, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Bind PythonLanguage language, + @Cached("createFor($node)") BoundaryCallData boundaryCallData, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Cached SequenceStorageNodes.ToArrayNode toArrayNode) { + Object state = BoundaryCallContext.enter(frame, boundaryCallData); + try { + // Note: we need actual recursion to trigger the stack overflow error like CPython. + return callRecursiveWithNodeTruffleBoundary(inliningTarget, arg, clsTuple, getTupleStorage, toArrayNode); + } finally { + BoundaryCallContext.exit(frame, boundaryCallData, state); + } + } + + @SuppressWarnings("truffle-static-method") + @Specialization(guards = "tupleCheck.execute(inliningTarget, clsTuple)", limit = "1") + boolean doRecursiveUncached(VirtualFrame frame, Object arg, Object clsTuple, @SuppressWarnings("unused") int depth, + @Bind Node inliningTarget, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Cached SequenceStorageNodes.ToArrayNode toArrayNode) { + assert this instanceof UnadoptableNode; + return loopRecursive(frame, arg, clsTuple, inliningTarget, getTupleStorage, toArrayNode, this, -1); + } + + @TruffleBoundary + private boolean callRecursiveWithNodeTruffleBoundary(Node inliningTarget, Object arg, Object clsTuple, TupleNodes.GetTupleStorage getTupleStorage, + SequenceStorageNodes.ToArrayNode toArrayNode) { + return loopRecursive(null, arg, clsTuple, inliningTarget, getTupleStorage, toArrayNode, getUncachedRecursive(), -1); + } + + private static boolean loopRecursive(VirtualFrame frame, Object arg, Object clsTuple, Node inliningTarget, TupleNodes.GetTupleStorage getTupleStorage, + SequenceStorageNodes.ToArrayNode toArrayNode, PyObjectRecursiveBinaryCheckNode node, int depth) { + for (Object cls : getTupleArray(inliningTarget, clsTuple, getTupleStorage, toArrayNode)) { + if (node.executeInternal(frame, arg, cls, depth)) { + return true; + } + } + return false; + } + + private static Object[] getTupleArray(Node inliningTarget, Object clsTuple, TupleNodes.GetTupleStorage getTupleStorage, SequenceStorageNodes.ToArrayNode toArrayNode) { + return toArrayNode.execute(inliningTarget, getTupleStorage.execute(inliningTarget, clsTuple)); + } + + @Idempotent + protected static int getNodeRecursionLimit(PythonLanguage language) { + return language.getEngineOption(PythonOptions.NodeRecursionLimit); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectReprAsObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectReprAsObjectNode.java index 961e07ca9c..e9e117200a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectReprAsObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectReprAsObjectNode.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.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Cached; @@ -97,18 +98,24 @@ static Object repr(VirtualFrame frame, Node inliningTarget, Object obj, @Cached GetObjectSlotsNode getSlots, @Cached CallSlotReprNode callSlot, @Cached(inline = false) ObjectNodes.DefaultObjectReprNode defaultRepr, + @Cached PyEnterRecursiveCallNode enterNode, @Cached PyUnicodeCheckNode checkNode, @Cached PRaiseNode raiseNode) { + PythonThreadState threadState = enterNode.enter(inliningTarget, ErrorMessages.MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_GETTING_REPR_OF_AN_OBJECT); TpSlots slots = getSlots.execute(inliningTarget, obj); - if (slots.tp_repr() == null) { - return defaultRepr.execute(frame, inliningTarget, obj); - } - Object result = callSlot.execute(frame, inliningTarget, slots.tp_repr(), obj); - assertNoJavaString(result); - if (checkNode.execute(inliningTarget, result)) { - return result; - } else { - throw raiseTypeError(inliningTarget, obj, raiseNode); + try { + if (slots.tp_repr() == null) { + return defaultRepr.execute(frame, inliningTarget, obj); + } + Object result = callSlot.execute(frame, inliningTarget, slots.tp_repr(), obj); + assertNoJavaString(result); + if (checkNode.execute(inliningTarget, result)) { + return result; + } else { + throw raiseTypeError(inliningTarget, result, raiseNode); + } + } finally { + PyEnterRecursiveCallNode.leave(threadState); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectStrAsObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectStrAsObjectNode.java index 0e7cd2e223..72872c9d89 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectStrAsObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectStrAsObjectNode.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 com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Cached; @@ -114,18 +115,24 @@ static Object str(VirtualFrame frame, Node inliningTarget, Object obj, @Cached GetObjectSlotsNode getSlots, @Cached CallSlotUnaryNode callSlot, @Cached PyObjectReprAsObjectNode repr, + @Cached PyEnterRecursiveCallNode enterNode, @Cached PyUnicodeCheckNode checkNode, @Cached PRaiseNode raiseNode) { TpSlots slots = getSlots.execute(inliningTarget, obj); if (slots.tp_str() == null) { return repr.execute(frame, inliningTarget, obj); } - Object result = callSlot.execute(frame, inliningTarget, slots.tp_str(), obj); - assertNoJavaString(result); - if (checkNode.execute(inliningTarget, result)) { - return result; - } else { - throw raiseTypeError(inliningTarget, raiseNode, result); + PythonThreadState threadState = enterNode.enter(inliningTarget, ErrorMessages.MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_GETTING_STR_OF_AN_OBJECT); + try { + Object result = callSlot.execute(frame, inliningTarget, slots.tp_str(), obj); + assertNoJavaString(result); + if (checkNode.execute(inliningTarget, result)) { + return result; + } else { + throw raiseTypeError(inliningTarget, raiseNode, result); + } + } finally { + PyEnterRecursiveCallNode.leave(threadState); } } 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/PyTZInfoCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTZInfoCheckNode.java new file mode 100644 index 0000000000..cc05e56e6a --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTZInfoCheckNode.java @@ -0,0 +1,90 @@ +/* + * 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.lib; + +import com.oracle.graal.python.builtins.modules.datetime.PTzInfo; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +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.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +/** Equivalent of CPython's {@code PyTZInfo_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyTZInfoCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyTZInfoCheckNodeGen.getUncached().execute(null, object); + } + + public abstract boolean execute(Node inliningTarget, Object object); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PTzInfo value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, com.oracle.graal.python.builtins.PythonBuiltinClassType.PTzInfo); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isTimeZone(value) && !interop.isDate(value) && !interop.isTime(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTimeCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTimeCheckNode.java new file mode 100644 index 0000000000..a7502b49e8 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTimeCheckNode.java @@ -0,0 +1,90 @@ +/* + * 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.lib; + +import com.oracle.graal.python.builtins.modules.datetime.PTime; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; +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.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; + +/** Equivalent of CPython's {@code PyTime_Check}. */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class PyTimeCheckNode extends Node { + public static boolean executeUncached(Object object) { + return PyTimeCheckNodeGen.getUncached().execute(null, object); + } + + public abstract boolean execute(Node inliningTarget, Object object); + + @Specialization + static boolean doManaged(@SuppressWarnings("unused") PTime value) { + return true; + } + + @Specialization + static boolean doNative(Node inliningTarget, PythonAbstractNativeObject value, + @Cached IsBuiltinObjectProfile profile) { + return profile.profileObject(inliningTarget, value, com.oracle.graal.python.builtins.PythonBuiltinClassType.PTime); + } + + @Specialization(guards = "isForeignObjectNode.execute(inliningTarget, value)", limit = "1") + static boolean doForeign(Node inliningTarget, Object value, + @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode, + @CachedLibrary("value") InteropLibrary interop) { + return interop.isTime(value) && !interop.isDate(value); + } + + @Fallback + static boolean doOther(@SuppressWarnings("unused") Object value) { + return false; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java index d813f4c9f8..6088c97134 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTraceBackPrint.java @@ -61,7 +61,6 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.code.PCode; import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; -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.traceback.PTraceback; @@ -78,7 +77,6 @@ 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.util.OverflowException; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -220,11 +218,6 @@ public static TruffleString classNameNoDot(TruffleString name) { return (i > 0) ? name.substringUncached(i + 1, len - i - 1, TS_ENCODING, true) : name; } - private static PCode getCode(PythonLanguage language, TracebackBuiltins.GetTracebackFrameNode getTbFrameNode, PTraceback tb) { - final PFrame pFrame = getTbFrameNode.execute(null, tb); - return PFactory.createCode(language, pFrame.getTarget()); - } - protected static PTraceback getNextTb(Node inliningTarget, TracebackBuiltins.MaterializeTruffleStacktraceNode materializeStNode, PTraceback traceback) { materializeStNode.execute(inliningTarget, traceback); return traceback.getNext(); @@ -263,8 +256,8 @@ protected static CharSequence getSourceLine(TruffleString fileName, int lineNo) final PythonContext context = PythonContext.get(null); TruffleFile file = null; try { - file = context.getEnv().getInternalTruffleFile(fileName.toJavaStringUncached()); - } catch (Exception e) { + file = context.getPublicTruffleFileRelaxed(fileName, PythonLanguage.T_DEFAULT_PYTHON_EXTENSIONS); + } catch (IllegalArgumentException | SecurityException | UnsupportedOperationException e) { return null; } String line = null; @@ -286,7 +279,7 @@ protected static CharSequence getSourceLine(TruffleString fileName, int lineNo) i++; } } - } catch (IOException ioe) { + } catch (IllegalArgumentException | IOException | SecurityException | UnsupportedOperationException e) { line = null; } return line; @@ -355,9 +348,8 @@ private static void printInternal(Node inliningTarget, TracebackBuiltins.GetTrac tb = getNextTb(inliningTarget, materializeStNode, tb); } EqualNode tstrEqNode = EqualNode.getUncached(); - PythonLanguage language = PythonLanguage.get(inliningTarget); while (tb != null) { - final PCode code = getCode(language, getTbFrameNode, tb); + final PCode code = getTbFrameNode.execute(null, tb).getCode(); if (lastFile == null || code.getFilename() == null || !tstrEqNode.execute(code.getFilename(), lastFile, TS_ENCODING) || lastLine == -1 || tb.getLineno() != lastLine || diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckExactNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckExactNode.java index e0280e028b..464979834a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckExactNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleCheckExactNode.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,9 +40,13 @@ */ package com.oracle.graal.python.lib; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectExactProfile; +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; @@ -70,6 +74,12 @@ static boolean doBuiltinTuple(@SuppressWarnings("unused") PTuple tuple) { return true; } + @Specialization + static boolean doNativeTuple(PythonAbstractNativeObject tuple, + @Cached(inline = false) IsBuiltinObjectExactProfile check) { + return check.profileObject(check, tuple, PythonBuiltinClassType.PTuple); + } + @Fallback static boolean doOther(@SuppressWarnings("unused") Object object) { return false; 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/lib/PyUnicodeFSDecoderNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyUnicodeFSDecoderNode.java index 46ff74669c..4a9223cb65 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyUnicodeFSDecoderNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyUnicodeFSDecoderNode.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,6 +42,7 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_8; import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary; import com.oracle.graal.python.builtins.objects.bytes.PBytes; @@ -61,6 +62,8 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.AbstractTruffleString; +import com.oracle.truffle.api.strings.TranscodingErrorHandler; import com.oracle.truffle.api.strings.TruffleString; import com.oracle.truffle.api.strings.TruffleString.Encoding; @@ -71,6 +74,9 @@ @GenerateUncached @GenerateInline(false) public abstract class PyUnicodeFSDecoderNode extends PNodeWithContext { + public static final TranscodingErrorHandler SURROGATE_ESCAPE_FROM_UTF8_TRANSCODING_ERROR_HANDLER = PyUnicodeFSDecoderNode::surrogateEscapeTranscodingError; + public static final TranscodingErrorHandler SURROGATE_ESCAPE_TO_UTF8_TRANSCODING_ERROR_HANDLER = PyUnicodeFSDecoderNode::surrogateEscapeToUTF8Handler; + public abstract TruffleString execute(Frame frame, Object object); @Specialization @@ -91,11 +97,17 @@ static TruffleString doPString(PString object, TruffleString doBytes(PBytes object, @CachedLibrary("object") PythonBufferAccessLibrary bufferLib, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Cached TruffleString.IsValidNode isValidNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, @Shared("byteIndexOfCP") @Cached TruffleString.ByteIndexOfCodePointNode byteIndexOfCodePointNode) { - // TODO PyUnicode_DecodeFSDefault - TruffleString utf8 = fromByteArrayNode.execute(bufferLib.getCopiedByteArray(object), Encoding.UTF_8, false); - return checkString(this, switchEncodingNode.execute(utf8, TS_ENCODING), byteIndexOfCodePointNode); + TruffleString utf8 = fromByteArrayNode.execute(bufferLib.getCopiedByteArray(object), UTF_8, false); + TruffleString str; + if (isValidNode.execute(utf8, UTF_8)) { + str = switchEncodingNode.execute(utf8, TS_ENCODING); + } else { + str = switchEncodingNode.execute(utf8, TS_ENCODING, SURROGATE_ESCAPE_FROM_UTF8_TRANSCODING_ERROR_HANDLER); + } + return checkString(this, str, byteIndexOfCodePointNode); } @Fallback @@ -114,4 +126,19 @@ private static TruffleString checkString(Node raisingNode, TruffleString str, Tr } return str; } + + private static TranscodingErrorHandler.ReplacementString surrogateEscapeTranscodingError(AbstractTruffleString sourceString, int byteIndex, int estimatedByteLength, + Encoding sourceEncoding, Encoding targetEncoding) { + assert sourceEncoding == UTF_8 && targetEncoding == TS_ENCODING; + int b = sourceString.readByteUncached(byteIndex, UTF_8); + assert b >= 0x80; + return new TranscodingErrorHandler.ReplacementString(TruffleString.fromCodePointUncached(0xdc00 | b, TS_ENCODING, true), 1); + } + + private static TranscodingErrorHandler.ReplacementString surrogateEscapeToUTF8Handler(AbstractTruffleString sourceString, int byteIndex, + @SuppressWarnings("unused") int estimatedByteLength, Encoding sourceEncoding, Encoding targetEncoding) { + assert sourceEncoding == TS_ENCODING && targetEncoding == UTF_8; + int codepoint = sourceString.codePointAtByteIndexUncached(byteIndex, TS_ENCODING); + return new TranscodingErrorHandler.ReplacementString(TruffleString.fromByteArrayUncached(new byte[]{(byte) codepoint}, TruffleString.Encoding.UTF_8), 4); + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/fastpath/PyNumberLshiftFastPathsBase.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/fastpath/PyNumberLshiftFastPathsBase.java index 3662fe3a00..69fca87b63 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/fastpath/PyNumberLshiftFastPathsBase.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/fastpath/PyNumberLshiftFastPathsBase.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,7 +47,7 @@ /** * Helper class with shared fast-paths. Must be public so that it is accessible by the Bytecode DSL - * generated code. TODO: unused due to GR-64005 + * generated code. */ @GenerateCached(false) public abstract class PyNumberLshiftFastPathsBase extends BinaryOpNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java index 73a3402ec7..9720d86aea 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/BuiltinNames.java @@ -207,6 +207,7 @@ private static TruffleString tsLiteral(String s) { public static final TruffleString T_INT = tsLiteral(J_INT); public static final String J_OPEN = "open"; + public static final TruffleString T_OPEN = tsLiteral(J_OPEN); public static final String J_STR = "str"; public static final TruffleString T_STR = tsLiteral(J_STR); @@ -394,7 +395,11 @@ private static TruffleString tsLiteral(String s) { public static final TruffleString T_SYS = tsLiteral("sys"); - public static final TruffleString T__SIGNAL = tsLiteral("_signal"); + public static final String J_SIGNAL = "signal"; + public static final TruffleString T_SIGNAL = tsLiteral(J_SIGNAL); + + public static final String J__SIGNAL = "_signal"; + public static final TruffleString T__SIGNAL = tsLiteral(J__SIGNAL); public static final String J__WEAKREF = "_weakref"; public static final TruffleString T__WEAKREF = tsLiteral(J__WEAKREF); @@ -489,6 +494,9 @@ private static TruffleString tsLiteral(String s) { public static final String J_HASHLIB = "_hashlib"; public static final TruffleString T_HASHLIB = tsLiteral(J_HASHLIB); + public static final String J_PYEXPAT = "pyexpat"; + public static final TruffleString T_PYEXPAT = tsLiteral(J_PYEXPAT); + public static final String J_MD5 = "_md5"; public static final String J_SHA1 = "_sha1"; 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 1014a90522..25a3cb9424 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 @@ -166,7 +166,6 @@ public abstract class ErrorMessages { public static final TruffleString CAN_ONLY_CONCAT_S_NOT_P_TO_S = tsLiteral("can only concatenate %s (not \"%p\") to %s"); public static final TruffleString CAN_ONLY_JOIN_ITERABLE = tsLiteral("can only join an iterable"); public static final TruffleString S_CANNOT_BE_NEGATIVE_INTEGER_D = tsLiteral("%s cannot be negative integer (%d)"); - public static final TruffleString S_CANNOT_HAVE_S = tsLiteral("%s cannot have %s"); public static final TruffleString S_EXPECTED_D_ARGUMENTS_GOT_D = tsLiteral("%s expected %d arguments, got %d"); public static final TruffleString CANNOT_CONVERT_DICT_UPDATE_SEQ = tsLiteral("cannot convert dictionary update sequence element #%d to a sequence"); @@ -261,6 +260,9 @@ public abstract class ErrorMessages { public static final TruffleString DESC_FOR_INDEX_S_FOR_S_DOESNT_APPLY_TO_P = tsLiteral("descriptor for index '%d' for %s doesn't apply to '%p' object"); public static final TruffleString DESC_S_FOR_N_DOESNT_APPLY_TO_N = tsLiteral("descriptor '%s' for '%N' objects doesn't apply to '%N' object"); public static final TruffleString GET_NONE_NONE_IS_INVALID = tsLiteral("__get__(None, None) is invalid"); + public static final TruffleString DESCRIPTOR_S_FOR_TYPE_S_NEEDS_EITHER_OBJ_OR_TYPE = tsLiteral("descriptor '%s' for type '%s' needs either an object or a type"); + public static final TruffleString DESCRIPTOR_S_FOR_TYPE_S_NEEDS_TYPE_NOT_P_AS_ARG_2 = tsLiteral("descriptor '%s' for type '%s' needs a type, not a '%p' as arg 2"); + public static final TruffleString DESCRIPTOR_S_REQUIRES_SUBTYPE_OF_S_BUT_RECEIVED_S = tsLiteral("descriptor '%s' requires a subtype of '%s' but received '%s'"); public static final TruffleString DESCRIPTOR_S_REQUIRES_S_OBJ_RECEIVED_P = tsLiteral("descriptor '%s' requires a '%s' object but received a '%p'"); public static final TruffleString DESCRIPTOR_REQUIRES_S_OBJ_RECEIVED_P = tsLiteral("descriptor requires a '%s' object but received a '%p'"); public static final TruffleString DESCRIPTOR_NEED_OBJ = tsLiteral("descriptor '%s' of '%s' object needs an argument"); @@ -441,7 +443,6 @@ public abstract class ErrorMessages { public static final TruffleString ITERATION_VALUE_MUST_BE_GREATER_THAN_ZERO = tsLiteral("iteration value must be greater than 0."); public static final TruffleString ITERATION_VALUE_IS_TOO_GREAT = tsLiteral("iteration value is too great."); public static final TruffleString KEY_LENGTH_MUST_BE_GREATER_THAN_ZERO = tsLiteral("key length must be greater than 0."); - public static final TruffleString KEYWORD_NAMES_MUST_BE_STR_GOT_P = tsLiteral("keyword names must be str, get %p"); public static final TruffleString KEYWORDS_S_MUST_BE_STRINGS = tsLiteral("keywords must be strings"); public static final TruffleString KLASS_ARG_IS_NOT_HOST_OBJ = tsLiteral("instanceof second argument '%p' is not a Java class"); public static final TruffleString LAZY_INITIALIZATION_FAILED = tsLiteral("lazy initialization of type %N failed"); @@ -474,6 +475,7 @@ public abstract class ErrorMessages { public static final TruffleString INPUTS_ARE_NOT_THE_SAME_LENGTH = tsLiteral("Inputs are not the same length"); public static final TruffleString MATH_DOMAIN_ERROR = tsLiteral("math domain error"); public static final TruffleString MATH_RANGE_ERROR = tsLiteral("math range error"); + public static final TruffleString NUMERICAL_RESULT_OUT_OF_RANGE = tsLiteral("Numerical result out of range"); public static final TruffleString MAX_MARSHAL_STACK_DEPTH = tsLiteral("Maximum marshal stack depth"); public static final TruffleString M = tsLiteral("%m"); public static final TruffleString MEMORYVIEW_INVALID_SLICE_KEY = tsLiteral("memoryview: invalid slice key"); @@ -625,7 +627,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"); @@ -828,6 +830,9 @@ public abstract class ErrorMessages { public static final TruffleString LINE_BUFFERING_ISNT_SUPPORTED = tsLiteral("line buffering (buffering=1) isn't supported in binary mode, the default buffer size will be used"); public static final TruffleString UNCLOSED_FILE = tsLiteral("unclosed file %r"); public static final TruffleString MAXIMUM_RECURSION_DEPTH_EXCEEDED = tsLiteral("maximum recursion depth exceeded"); + public static final TruffleString MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_GETTING_REPR_OF_AN_OBJECT = tsLiteral("maximum recursion depth exceeded while getting the repr of an object"); + public static final TruffleString MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_GETTING_STR_OF_AN_OBJECT = tsLiteral("maximum recursion depth exceeded while getting the str of an object"); + public static final TruffleString MAXIMUM_RECURSION_DEPTH_EXCEEDED_WHILE_TRAVERSING_S_NODE = tsLiteral("maximum recursion depth exceeded while traversing '%s' node"); public static final TruffleString S_IS_LESS_THAN_MINIMUM = tsLiteral("%s is less than minimum"); public static final TruffleString S_IS_GREATER_THAN_MAXIUMUM = tsLiteral("%s is greater than maxiumum"); public static final TruffleString S_SHOULD_BE_INTEGER_NOT_P = tsLiteral("%s should be integer, not %p"); @@ -1193,6 +1198,9 @@ public abstract class ErrorMessages { public static final TruffleString UNABLE_TO_LOAD_LIBCRYPT = tsLiteral( "Unable to load libcrypt library. Please install libxcrypt-compat (RPM-based distributions) package or libcrypt1 (DEB-based distributions)."); public static final TruffleString FAILED_TO_REPARSE_BYTECODE_FILE = tsLiteral("Failed to reload bytecode file. Use --python.KeepBytecodeInMemory to keep serialized bytecode in memory"); + public static final TruffleString INTERN_MUST_BE_A_DICTIONARY = tsLiteral("intern must be a dictionary"); + public static final TruffleString PARSING_FINISHED = tsLiteral("parsing finished"); + public static final TruffleString NAMESPACE_SEPARATOR_MUST_BE = tsLiteral("namespace_separator must be at most one character, omitted, or None"); // ssl error messages public static final TruffleString SSL_ERR_DECODING_PEM_FILE_S = tsLiteral("Error decoding PEM-encoded file: %s"); @@ -1236,8 +1244,6 @@ public abstract class ErrorMessages { public static final TruffleString WARN_IGNORE_UNIMPORTABLE_BREAKPOINT_S = tsLiteral("Ignoring unimportable $PYTHONBREAKPOINT: \"%s\""); public static final TruffleString WARN_DEPRECTATED_SYS_CHECKINTERVAL = tsLiteral("sys.getcheckinterval() and sys.setcheckinterval() " + "are deprecated. Use sys.getswitchinterval() instead."); - public static final TruffleString WARN_CURRENT_FRAMES_MULTITHREADED = tsLiteral( - "GraalPy doesn't support obtaining frames of other threads. That means python debuggers can only see the currently stopped thread"); public static final TruffleString WARN_ENCODING_ARGUMENT_NOT_SPECIFIED = tsLiteral("'encoding' argument not specified"); public static final TruffleString WARN_DELEGATION_OF_INT_TO_TRUNC_IS_DEPRECATED = tsLiteral("The delegation of int() to __trunc__ is deprecated."); @@ -1296,9 +1302,8 @@ public abstract class ErrorMessages { public static final TruffleString NULL_ARG_INTERNAL = tsLiteral("null argument to internal routine"); - public static final TruffleString NFI_NOT_AVAILABLE = tsLiteral("GraalPy option '%s' is set to '%s, but the 'nfi' language, which is required for this feature, is not available. " + - "If this is a GraalPy standalone distribution: this indicates internal error. If GraalPy was used as a Maven dependency: " + - "are you missing a runtime dependency 'org.graalvm.truffle:truffle-nfi-libffi', which should be a dependency of 'org.graalvm.polyglot:python{-community}'?"); + public static final TruffleString NATIVE_EXTENSIONS_VIRTUAL_THREAD = tsLiteral("Python native extensions cannot be used from Java virtual threads. " + + "Run Python code that may load or call native extensions on a platform thread."); // AST Validator public static final TruffleString ANN_ASSIGN_WITH_SIMPLE_NON_NAME_TARGET = tsLiteral("AnnAssign with simple non-Name target"); @@ -1360,9 +1365,11 @@ public abstract class ErrorMessages { public static final TruffleString INDEX_EXCEEDS_INT = tsLiteral("index exceeds integer size"); public static final TruffleString X_NOT_IN_SEQUENCE = tsLiteral("sequence.index(x): x not in sequence"); - public static final TruffleString ASYNC_FOR_NO_AITER = tsLiteral("'async for' requires object with __aiter__ method, got %N"); + public static final TruffleString ASYNC_FOR_NO_AITER = tsLiteral("'async for' requires an object with __aiter__ method, got %N"); + public static final TruffleString OBJECT_NOT_ASYNC_ITERABLE = tsLiteral("'%p' object is not an async iterable"); public static final TruffleString ASYNC_FOR_NO_ANEXT_INITIAL = tsLiteral("'async for' received an object from __aiter__ that does not implement __anext__: %p"); public static final TruffleString ASYNC_FOR_NO_ANEXT_ITERATION = tsLiteral("'async for' requires an iterator with __anext__ method, got %p"); + public static final TruffleString AITER_RETURNED_NOT_ASYNC_ITERATOR = tsLiteral("aiter() returned not an async iterator of type '%N'"); public static final TruffleString CANNOT_REUSE_ASEND = tsLiteral("cannot reuse already awaited __anext__()/asend()"); public static final TruffleString OBJECT_NOT_ASYNCGEN = tsLiteral("'%p' object is not an async generator"); @@ -1578,4 +1585,7 @@ public abstract class ErrorMessages { public static final TruffleString NO_SUCH_GROUP = tsLiteral("no such group"); public static final TruffleString WEAKLY_REFERENCED_OBJECT_NO_LONGER_EXISTS = tsLiteral("weakly-referenced object no longer exists"); 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 5aca3355ed..5a9a9f2c84 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 @@ -40,6 +40,7 @@ */ package com.oracle.graal.python.nodes; +import static com.oracle.graal.python.builtins.objects.object.PythonObject.HAS_DICT; import static com.oracle.graal.python.builtins.objects.object.PythonObject.HAS_MATERIALIZED_DICT; import static com.oracle.graal.python.nodes.BuiltinNames.J___GRAALPYTHON_INTEROP_BEHAVIOR__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___BASICSIZE__; @@ -50,9 +51,15 @@ import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___WEAKLISTOFFSET__; import com.oracle.graal.python.builtins.objects.PythonAbstractObject; +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; @@ -62,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; @@ -100,7 +108,6 @@ public final class HiddenAttr { public static final HiddenAttr NATIVE_SLOTS = new HiddenAttr("__native_slots__"); public static final HiddenAttr INSTANCESHAPE = new HiddenAttr("instanceshape"); public static final HiddenAttr STRUCTSEQ_FIELD_NAMES = new HiddenAttr("struct_seq_field_names"); - public static final HiddenAttr PSTRING_NATIVE_DATA = new HiddenAttr("native_string_data"); public static final HiddenAttr PSTRING_UTF8 = new HiddenAttr("utf8"); public static final HiddenAttr PSTRING_WCHAR = new HiddenAttr("wchar"); @@ -114,6 +121,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(); @@ -136,6 +149,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); } @@ -169,13 +183,25 @@ 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) { - setShapeFlagsNode.executeAdd(self, HAS_MATERIALIZED_DICT); + assert !attr.hasLongValue(); + setShapeFlagsNode.executeAdd(self, HAS_DICT); + if (isGenericDict(self, value)) { + setShapeFlagsNode.executeAdd(self, HAS_MATERIALIZED_DICT); + } putNode.execute(self, DICT.key, value); } + private static boolean isGenericDict(PythonObject self, Object value) { + if (value instanceof PDict dict && dict.getDictStorage() instanceof DynamicObjectStorage dynamicStorage) { + return dynamicStorage.getStore() != self; + } + return true; + } + @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); } @@ -193,4 +219,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/PConstructAndRaiseNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PConstructAndRaiseNode.java index 6a0c120956..566c350ed5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PConstructAndRaiseNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PConstructAndRaiseNode.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 @@ -55,6 +55,7 @@ import com.oracle.graal.python.builtins.objects.ssl.SSLErrorCode; import com.oracle.graal.python.nodes.PConstructAndRaiseNodeGen.LazyNodeGen; import com.oracle.graal.python.nodes.call.CallNode; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; import com.oracle.graal.python.runtime.PythonContext; @@ -231,15 +232,27 @@ public final PException raiseOSError(VirtualFrame frame, int errno, TruffleStrin } public final PException raiseOSErrorFromPosixException(VirtualFrame frame, PosixException e) { - return raiseOSErrorInternal(frame, createOsErrorArgs(e.getErrorCode(), e.getMessageAsTruffleString())); + if (e instanceof PosixErrnoException errnoException) { + return raiseOSErrorInternal(frame, createOsErrorArgs(errnoException.getErrorCode(), errnoException.getMessageAsTruffleString())); + } + assert e instanceof UnsupportedPosixFeatureException; + return raiseOSErrorUnsupported(frame, (UnsupportedPosixFeatureException) e); } public final PException raiseOSErrorFromPosixException(VirtualFrame frame, PosixException e, Object filename1) { - return raiseOSErrorInternal(frame, createOsErrorArgs(e.getErrorCode(), e.getMessageAsTruffleString(), filename1)); + if (e instanceof PosixErrnoException errnoException) { + return raiseOSErrorInternal(frame, createOsErrorArgs(errnoException.getErrorCode(), errnoException.getMessageAsTruffleString(), filename1)); + } + assert e instanceof UnsupportedPosixFeatureException; + return raiseOSErrorUnsupported(frame, (UnsupportedPosixFeatureException) e); } public final PException raiseOSErrorFromPosixException(VirtualFrame frame, PosixException e, Object filename1, Object filename2) { - return raiseOSErrorInternal(frame, createOsErrorArgs(e.getErrorCode(), e.getMessageAsTruffleString(), filename1, filename2)); + if (e instanceof PosixErrnoException errnoException) { + return raiseOSErrorInternal(frame, createOsErrorArgs(errnoException.getErrorCode(), errnoException.getMessageAsTruffleString(), filename1, filename2)); + } + assert e instanceof UnsupportedPosixFeatureException; + return raiseOSErrorUnsupported(frame, (UnsupportedPosixFeatureException) e); } public final PException raiseOSErrorUnsupported(VirtualFrame frame, UnsupportedPosixFeatureException e) { 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..5de77dd4d3 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 @@ -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 @@ -52,8 +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; import com.oracle.graal.python.builtins.objects.common.EmptyStorage; @@ -109,7 +107,6 @@ import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Idempotent; import com.oracle.truffle.api.exception.AbstractTruffleException; -import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.UnexpectedResultException; import com.oracle.truffle.api.strings.TruffleString; @@ -524,22 +521,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; - } else if (value instanceof NativePointer nativePointer) { - return nativePointer.isNull(); - } else { - return lib.isNull(value); - } - } - /* CPython tests that tp_iter is dict_iter */ public static boolean hasBuiltinDictIter(Node inliningTarget, PDict dict, GetPythonObjectClassNode getClassNode, GetCachedTpSlotsNode getSlots) { return isBuiltinDict(dict) || getSlots.execute(inliningTarget, getClassNode.execute(inliningTarget, dict)).tp_iter() == DictBuiltins.SLOTS.tp_iter(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java index 1e35eeef3f..86ba40c9a3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java @@ -58,7 +58,6 @@ 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.nodes.EncapsulatingNodeReference; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.strings.TruffleString; @@ -275,11 +274,7 @@ private static PException raiseExceptionObjectStatic(Node raisingNode, Object ex // No @InliningCutoff, done in callers already public static PException raiseExceptionObjectStatic(Node raisingNode, Object exc, boolean withJavaStacktrace) { - if (raisingNode != null && raisingNode.isAdoptable()) { - throw PException.fromObject(exc, raisingNode, withJavaStacktrace); - } else { - throw PException.fromObject(exc, EncapsulatingNodeReference.getCurrent().get(), withJavaStacktrace); - } + throw PException.fromObjectFixUncachedLocation(exc, raisingNode, withJavaStacktrace); } public static PRaiseNode getUncached() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java index bffa16044b..a13d0082df 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/StringLiterals.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 @@ -136,7 +136,6 @@ public abstract class StringLiterals { public static final TruffleString T_VERSION = tsLiteral("version"); public static final String J_DEFAULT = "default"; public static final TruffleString T_DEFAULT = tsLiteral(J_DEFAULT); - public static final String J_NFI_LANGUAGE = "nfi"; public static final TruffleString T_ID = tsLiteral("id"); public static final TruffleString T_SITE = tsLiteral("site"); public static final TruffleString T_GRAALPYTHON = tsLiteral("graalpy"); 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/builtins/objects/cext/capi/CApiGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowReleaseCallback.java similarity index 69% 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/nodes/arrow/ArrowReleaseCallback.java index 86adf483e4..85082a0d49 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/nodes/arrow/ArrowReleaseCallback.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,26 @@ * 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.nodes.arrow; -import com.oracle.graal.python.builtins.objects.cext.common.NativePointer; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; -public abstract class CApiGuards { +import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport; +import com.oracle.truffle.api.CompilerDirectives; - public static boolean isPrimitiveNativeWrapper(Object object) { - return object instanceof PrimitiveNativeWrapper; - } - - public static boolean isNativeWrapper(Object object) { - return object instanceof PythonNativeWrapper || object instanceof PyCFunctionWrapper; - } +public final class ArrowReleaseCallback { + private static final MethodHandle HANDLE = NativeAccessSupport.createDowncallHandle( + MethodType.methodType(void.class, long.class, long.class), false); - public static boolean isNativeNull(Object object) { - return object instanceof NativePointer nativePointer && nativePointer.isNull(); + private ArrowReleaseCallback() { } - public static boolean isSpecialSingleton(Object delegate) { - return CApiContext.getSingletonNativeWrapperIdx(delegate) != -1; + public static void execute(long releaseCallback, long baseStructure) { + try { + HANDLE.invokeExact(releaseCallback, baseStructure); + } catch (Throwable 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/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/capsule/ArrowArrayCapsuleDestructor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowArrayCapsuleDestructor.java index ec481d2cab..9fff61038b 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 @@ -40,46 +40,47 @@ */ package com.oracle.graal.python.nodes.arrow.capsule; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import com.oracle.graal.python.annotations.CApiUpcallTarget; 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.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode; import com.oracle.graal.python.nodes.arrow.ArrowArray; -import com.oracle.graal.python.nodes.arrow.InvokeArrowReleaseCallbackNode; +import com.oracle.graal.python.nodes.arrow.ArrowReleaseCallback; 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.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.graal.python.runtime.nativeaccess.NativeMemory; +import com.oracle.truffle.api.CompilerAsserts; -@ExportLibrary(InteropLibrary.class) -public class ArrowArrayCapsuleDestructor implements TruffleObject { +public final class ArrowArrayCapsuleDestructor { + private static final MethodHandle HANDLE_EXECUTE; - @ExportMessage - boolean isExecutable() { - return true; + static { + try { + HANDLE_EXECUTE = MethodHandles.lookup().findStatic(ArrowArrayCapsuleDestructor.class, "execute", + MethodType.methodType(void.class, long.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } } - @ExportMessage - static class Execute { + private ArrowArrayCapsuleDestructor() { + } + + public static MethodHandle getMethodHandle() { + return HANDLE_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)); + @CApiUpcallTarget + private static void execute(long capsulePointer) { + CompilerAsserts.neverPartOfCompilation(); + PythonContext ctx = PythonContext.get(null); + ctx.ensureNativeAccess(); + Object capsule = NativeToPythonInternalNode.executeUncached(capsulePointer, false); + long capsuleNamePointer = ctx.stringToNativeUtf8Bytes(ArrowArray.CAPSULE_NAME, false); + try { + var arrowArray = ArrowArray.wrap(PyCapsuleGetPointerNode.executeUncached(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 @@ -90,19 +91,11 @@ static Object doRelease(ArrowArrayCapsuleDestructor self, Object[] args, * semantics */ if (!arrowArray.isReleased()) { - invokeReleaseCallbackNode.get(inliningTarget).executeCached(arrowArray.releaseCallback(), arrowArray.memoryAddress()); + ArrowReleaseCallback.execute(arrowArray.releaseCallback(), arrowArray.memoryAddress()); } - PythonContext.get(inliningTarget).getUnsafe().freeMemory(arrowArray.memoryAddress()); - return PNone.NO_VALUE; - } - - @Fallback - static Object doError(ArrowArrayCapsuleDestructor self, Object[] args) { - throw CompilerDirectives.shouldNotReachHere(); - } - - static boolean isPointer(Object[] args, InteropLibrary interopLib) { - return args.length == 1 && interopLib.isPointer(args[0]); + NativeMemory.free(arrowArray.memoryAddress()); + } finally { + NativeMemory.free(capsuleNamePointer); } } } 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..8fbbe775f1 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 @@ -40,62 +40,55 @@ */ package com.oracle.graal.python.nodes.arrow.capsule; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import com.oracle.graal.python.annotations.CApiUpcallTarget; 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.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode; +import com.oracle.graal.python.nodes.arrow.ArrowReleaseCallback; 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.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.graal.python.runtime.nativeaccess.NativeMemory; +import com.oracle.truffle.api.CompilerAsserts; -@ExportLibrary(InteropLibrary.class) -public class ArrowSchemaCapsuleDestructor implements TruffleObject { +public final class ArrowSchemaCapsuleDestructor { + private static final MethodHandle HANDLE_EXECUTE; - @ExportMessage - boolean isExecutable() { - return true; + static { + try { + HANDLE_EXECUTE = MethodHandles.lookup().findStatic(ArrowSchemaCapsuleDestructor.class, "execute", + MethodType.methodType(void.class, long.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } } - @ExportMessage - static class Execute { + private ArrowSchemaCapsuleDestructor() { + } + + public static MethodHandle getMethodHandle() { + return HANDLE_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)); + @CApiUpcallTarget + private static void execute(long capsulePointer) { + CompilerAsserts.neverPartOfCompilation(); + PythonContext ctx = PythonContext.get(null); + ctx.ensureNativeAccess(); + Object capsule = NativeToPythonInternalNode.executeUncached(capsulePointer, false); + long capsuleNamePointer = ctx.stringToNativeUtf8Bytes(ArrowSchema.CAPSULE_NAME, false); + try { + var arrowSchema = ArrowSchema.wrap(PyCapsuleGetPointerNode.executeUncached(capsule, capsuleNamePointer)); if (!arrowSchema.isReleased()) { - invokeReleaseCallbackNode.get(inliningTarget).executeCached(arrowSchema.releaseCallback(), arrowSchema.memoryAddress()); + ArrowReleaseCallback.execute(arrowSchema.releaseCallback(), arrowSchema.memoryAddress()); } - PythonContext.get(inliningTarget).getUnsafe().freeMemory(arrowSchema.memoryAddress()); - return PNone.NO_VALUE; - } - - @Fallback - static Object doError(ArrowSchemaCapsuleDestructor self, Object[] args) { - throw CompilerDirectives.shouldNotReachHere(); - } - - static boolean isPointer(Object[] args, InteropLibrary interopLib) { - return args.length == 1 && interopLib.isPointer(args[0]); + NativeMemory.free(arrowSchema.memoryAddress()); + } finally { + NativeMemory.free(capsuleNamePointer); } } - } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java index 5f8aa4aa6b..4f06d56c5d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.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 @@ -66,7 +66,7 @@ Object doIt(VirtualFrame frame, Object object, @Bind Node inliningTarget, @Cached GetClassNode getClassNode, @Cached GetCachedTpSlotsNode getSlotsNode, - @Cached MergedObjectTypeModuleGetAttributeInnerNode innerNode) { + @Cached MergedObjectTypeModuleGetFixedAttributeNode innerNode) { Object type = getClassNode.execute(inliningTarget, object); TpSlots slots = getSlotsNode.execute(inliningTarget, type); return innerNode.execute(frame, inliningTarget, object, key, type, slots); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java index 5f73173525..93885bcf16 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java @@ -44,7 +44,6 @@ 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.str.StringUtils; import com.oracle.graal.python.builtins.objects.type.MroShape; import com.oracle.graal.python.builtins.objects.type.MroShape.MroShapeLookupResult; import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass; @@ -56,6 +55,7 @@ import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.StacktracelessCheckedException; import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage; +import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -93,16 +93,17 @@ public final Object execute(Object klass) { protected abstract Object executeInternal(Object klass) throws MROChangedException; + @ImportStatic(PythonUtils.class) @GenerateUncached - @GenerateInline(false) // footprint reduction 36 -> 17 + @GenerateInline(false) public abstract static class Dynamic extends PNodeWithContext { public abstract Object execute(Object klass, TruffleString key); - @Specialization(guards = "equalNode.execute(inliningTarget, key, cachedKey)", limit = "2") + @Specialization(guards = "equalNode.execute(key, cachedKey, TS_ENCODING)", limit = "2") static Object lookupConstantMROEquals(Object klass, TruffleString key, @Bind Node inliningTarget, @Cached("key") TruffleString cachedKey, - @Cached @Shared StringUtils.EqualNode equalNode, + @Cached @Shared TruffleString.EqualNode equalNode, @Cached("create(cachedKey)") LookupAttributeInMRONode lookup) { return lookup.execute(klass); } @@ -113,7 +114,7 @@ static Object lookupConstantMROEquals(Object klass, TruffleString key, static Object lookupGeneric(Object klass, TruffleString key, @Bind Node inliningTarget, @Cached InlinedConditionProfile pbctProfile, - @Cached ReadAttributeFromPythonObjectNode readPBCTAttrNode, + @Cached(inline = false) ReadAttributeFromPythonObjectNode readPBCTAttrNode, @Cached GetMroStorageNode getMroNode, @Cached ReadAttributeFromObjectNode readAttrNode) { if (pbctProfile.profile(inliningTarget, klass instanceof PythonBuiltinClassType)) { @@ -286,7 +287,7 @@ static PythonBuiltinClassType findOwnerInMro(Python3Core core, PythonBuiltinClas Object lookupPBCTCachedOwner(PythonBuiltinClassType klass, TruffleString key, boolean skipNonStaticBases, @Cached("klass") PythonBuiltinClassType cachedKlass, @Cached("findOwnerInMro(getContext(), cachedKlass, key)") PythonBuiltinClassType ownerKlass, - @Shared @Cached ReadAttributeFromPythonObjectNode readAttrNode) { + @Shared @Cached(inline = false) ReadAttributeFromPythonObjectNode readAttrNode) { if (ownerKlass == null) { return PNone.NO_VALUE; } else { @@ -296,7 +297,7 @@ Object lookupPBCTCachedOwner(PythonBuiltinClassType klass, TruffleString key, bo @Specialization(replaces = "lookupPBCTCachedOwner") Object lookupPBCTGeneric(PythonBuiltinClassType klass, TruffleString key, boolean skipNonStaticBases, - @Shared @Cached ReadAttributeFromPythonObjectNode readAttrNode) { + @Shared @Cached(inline = false) ReadAttributeFromPythonObjectNode readAttrNode) { return findAttr(PythonContext.get(this), klass, key, readAttrNode); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetFixedAttributeNode.java similarity index 71% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetAttributeNode.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetFixedAttributeNode.java index 233ed8654a..33ec150b59 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetAttributeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetFixedAttributeNode.java @@ -47,9 +47,7 @@ import com.oracle.graal.python.builtins.objects.module.ModuleBuiltins; 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.str.StringNodes.CastToTruffleStringChecked1Node; 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.TpSlots.GetObjectSlotsNode; import com.oracle.graal.python.builtins.objects.type.TypeBuiltins; import com.oracle.graal.python.builtins.objects.type.slots.TpSlot; @@ -59,14 +57,12 @@ import com.oracle.graal.python.nodes.ErrorMessages; 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.runtime.exception.PException; import com.oracle.truffle.api.CompilerAsserts; 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.Idempotent; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; @@ -80,32 +76,14 @@ * {@link com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode}, * {@link com.oracle.graal.python.builtins.objects.type.TypeBuiltins.GetattributeNode} and * {@link com.oracle.graal.python.builtins.objects.module.ModuleBuiltins.ModuleGetattributeNode} to - * reduce code size by about 3x for host inlining + * reduce code size for host inlining. + * + * Fixed-key version used by {@link GetFixedAttributeNode}. The key is passed via execute for + * inlining, but the caller is expected to keep its identity stable for the lifetime of the node. */ -@GenerateUncached -@GenerateInline -@GenerateCached(false) -public abstract class MergedObjectTypeModuleGetAttributeNode extends PNodeWithContext { - - public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, Object keyObj); - - @Specialization - static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, Object keyObj, - @Cached CastToTruffleStringChecked1Node castToString, - @Cached GetClassNode getClassNode, - @Cached GetCachedTpSlotsNode getSlotsNode, - @Cached MergedObjectTypeModuleGetAttributeInnerNode innerNode) { - TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); - Object type = getClassNode.execute(inliningTarget, object); - TpSlots slots = getSlotsNode.execute(inliningTarget, type); - return innerNode.execute(frame, inliningTarget, object, key, type, slots); - } -} - -@GenerateUncached @GenerateInline @GenerateCached(false) -abstract class MergedObjectTypeModuleGetAttributeInnerNode extends PNodeWithContext { +public abstract class MergedObjectTypeModuleGetFixedAttributeNode extends PNodeWithContext { public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, TpSlots slots); @@ -122,7 +100,7 @@ static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, Truff @Cached("slots.tp_getattro()") TpSlot cachedSlot, // Common @Cached GetObjectSlotsNode getDescrSlotsNode, - @Cached LookupAttributeInMRONode.Dynamic lookup, + @Cached(value = "create(key)", inline = false) LookupAttributeInMRONode lookup, @Cached InlinedConditionProfile hasDescProfile, @Cached InlinedConditionProfile hasDescrGetProfile, @Cached InlinedConditionProfile hasValueProfile, @@ -130,69 +108,78 @@ static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, Truff @Cached CallSlotDescrGet.Lazy callSlotDescrGet, // Specific to a given tp_getattro, some should probably be lazy @Cached ReadAttributeFromObjectNode readAttributeOfObjectNode, - @Cached LookupAttributeInMRONode.Dynamic readAttributeOfClassNode, + @Cached(value = "create(key)", inline = false) LookupAttributeInMRONode readAttributeOfClassNode, @Cached GetObjectSlotsNode getValueSlotsNode, @Cached InlinedBranchProfile hasNonDescriptorValueProfile, @Cached CallSlotDescrGet.Lazy callSlotValueGet, @Cached ModuleBuiltins.LazyHandleGetattrExceptionNode handleException) { - assert hasNoGetAttr(object); + assert hasNoGetAttr(type); try { - Object descr = lookup.execute(type, key); + Object descr = lookup.execute(type); boolean hasDescr = hasDescProfile.profile(inliningTarget, descr != PNone.NO_VALUE); TpSlot get = null; boolean hasDescrGet = false; + boolean getValue = true; if (hasDescr) { var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); get = descrSlots.tp_descr_get(); hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null); if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { - return callSlotDescrGet.get(inliningTarget).executeCached(frame, get, descr, object, type); + // fall through to callSlotDescrGet below to avoid duplicating the call site + getValue = false; } } - // The main difference between all 3 nodes - Object value; - if (cachedSlot != TypeBuiltins.SLOTS.tp_getattro()) { - // ObjectBuiltins.SLOTS.tp_getattro() || ModuleBuiltins.SLOTS.tp_getattro() - value = readAttributeOfObjectNode.execute(object, key); - if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { - return value; - } - } else { - // TypeBuiltins.SLOTS.tp_getattro() - value = readAttributeOfClassNode.execute(object, key); - if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { - var valueGet = getValueSlotsNode.execute(inliningTarget, value).tp_descr_get(); - if (valueGet == null) { - hasNonDescriptorValueProfile.enter(inliningTarget); + if (getValue) { + // The main difference between all 3 nodes + if (cachedSlot != TypeBuiltins.SLOTS.tp_getattro()) { + // ObjectBuiltins.SLOTS.tp_getattro() || ModuleBuiltins.SLOTS.tp_getattro() + Object value = readAttributeOfObjectNode.execute(object, key); + if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { return value; - } else { - return callSlotValueGet.get(inliningTarget).executeCached(frame, valueGet, value, PNone.NO_VALUE, object); + } + } else { + // TypeBuiltins.SLOTS.tp_getattro() + Object value = readAttributeOfClassNode.execute(object); + if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { + var valueGet = getValueSlotsNode.execute(inliningTarget, value).tp_descr_get(); + if (valueGet == null) { + hasNonDescriptorValueProfile.enter(inliningTarget); + return value; + } else { + return callSlotValueGet.get(inliningTarget).executeCached(frame, valueGet, value, PNone.NO_VALUE, object); + } } } } if (hasDescr) { - if (!hasDescrGet) { - return descr; - } else { + if (hasDescrGet) { return callSlotDescrGet.get(inliningTarget).executeCached(frame, get, descr, object, type); + } else { + return descr; } } - TruffleString errorMessage = cachedSlot != TypeBuiltins.SLOTS.tp_getattro() ? ErrorMessages.OBJ_P_HAS_NO_ATTR_S : ErrorMessages.TYPE_N_HAS_NO_ATTR; + TruffleString errorMessage = cachedSlot == TypeBuiltins.SLOTS.tp_getattro() ? ErrorMessages.TYPE_N_HAS_NO_ATTR : ErrorMessages.OBJ_P_HAS_NO_ATTR_S; throw raiseNode.raiseAttributeError(inliningTarget, errorMessage, object, key); } catch (PException e) { // Extra behavior for module.__getattribute__ if (cachedSlot == ModuleBuiltins.SLOTS.tp_getattro()) { - return handleException.get(inliningTarget).execute(frame, (PythonModule) object, key, e); + return handleModuleException(frame, inliningTarget, object, key, e, handleException); } else { throw e; } } } + @InliningCutoff + private static Object handleModuleException(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, PException e, + ModuleBuiltins.LazyHandleGetattrExceptionNode handleException) { + return handleException.get(inliningTarget).execute(frame, (PythonModule) object, key, e); + } + @InliningCutoff @Specialization(replaces = "doIt") static Object doGeneric(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, @SuppressWarnings("unused") Object type, TpSlots slots, @@ -210,8 +197,8 @@ static boolean isObjectTypeModuleGetAttribute(TpSlot slot) { return slot == ObjectBuiltins.SLOTS.tp_getattro() || slot == TypeBuiltins.SLOTS.tp_getattro() || slot == ModuleBuiltins.SLOTS.tp_getattro(); } - static boolean hasNoGetAttr(Object obj) { + static boolean hasNoGetAttr(Object type) { CompilerAsserts.neverPartOfCompilation("only used in asserts"); - return LookupAttributeInMRONode.Dynamic.getUncached().execute(GetClassNode.executeUncached(obj), T___GETATTR__) == PNone.NO_VALUE; + return LookupAttributeInMRONode.Dynamic.getUncached().execute(type, T___GETATTR__) == PNone.NO_VALUE; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromModuleNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromModuleNode.java index bffff3949e..876b114323 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromModuleNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromModuleNode.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 @@ -41,7 +41,7 @@ package com.oracle.graal.python.nodes.attributes; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItemStringKey; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; @@ -74,8 +74,9 @@ public static ReadAttributeFromModuleNode getUncached() { static Object readModuleAttribute(PythonModule object, TruffleString key, @Bind Node inliningTarget, @Cached GetDictIfExistsNode getDict, - @Cached HashingStorageGetItem getItem) { + @Cached HashingStorageGetItemStringKey getItem) { var dict = getDict.execute(object); + assert object.checkDictFlags(dict); Object value = getItem.execute(inliningTarget, dict.getDictStorage(), key); if (value == null) { return PNone.NO_VALUE; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java index ea138dbaf4..07bc762e5e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java @@ -42,15 +42,14 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; -import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItemStringKey; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; -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.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.NeverDefault; @@ -65,7 +64,7 @@ */ @ReportPolymorphism @GenerateUncached -@GenerateInline(false) // footprint reduction 64 -> 47 +@GenerateInline(false) public abstract class ReadAttributeFromObjectNode extends PNodeWithContext { @NeverDefault @@ -87,13 +86,14 @@ public static ReadAttributeFromObjectNode getUncached() { @Specialization static Object readObjectAttribute(PythonObject object, TruffleString key, @Bind Node inliningTarget, - @Cached InlinedConditionProfile profileHasDict, - @Exclusive @Cached GetDictIfExistsNode getDict, - @Cached ReadAttributeFromPythonObjectNode readAttributeFromPythonObjectNode, - @Exclusive @Cached HashingStorageGetItem getItem) { + @Shared @Cached InlinedConditionProfile profileHasDict, + @Shared @Cached GetDictIfExistsNode getDict, + @Shared @Cached(inline = true) ReadAttributeFromPythonObjectNode readAttributeFromPythonObjectNode, + @Shared @Cached HashingStorageGetItemStringKey getItem) { var dict = getDict.execute(object); + assert object.checkDictFlags(dict); if (profileHasDict.profile(inliningTarget, dict == null)) { - return readAttributeFromPythonObjectNode.execute(object, key); + return readAttributeFromPythonObjectNode.execute(inliningTarget, object, key); } else { Object value = getItem.execute(inliningTarget, dict.getDictStorage(), key); if (value == null) { @@ -107,11 +107,11 @@ static Object readObjectAttribute(PythonObject object, TruffleString key, @Specialization static Object readNativeObject(PythonAbstractNativeObject object, TruffleString key, @Bind Node inliningTarget, - @Exclusive @Cached GetDictIfExistsNode getDict, - @Exclusive @Cached HashingStorageGetItem getItem) { + @Shared @Cached GetDictIfExistsNode getDict, + @Shared @Cached HashingStorageGetItemStringKey getItem) { PDict dict = getDict.execute(object); if (dict != null) { - Object result = getItem.execute(null, inliningTarget, dict.getDictStorage(), key); + Object result = getItem.execute(inliningTarget, dict.getDictStorage(), key); if (result != null) { return result; } @@ -120,7 +120,6 @@ static Object readNativeObject(PythonAbstractNativeObject object, TruffleString } // foreign object or primitive - @InliningCutoff @Specialization(guards = {"!isPythonObject(object)", "!isNativeObject(object)"}) static Object readForeignOrPrimitive(Object object, TruffleString key) { // Foreign members are tried after the regular attribute lookup, see diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromPythonObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromPythonObjectNode.java index 4e71c58b60..528520e27b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromPythonObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromPythonObjectNode.java @@ -45,18 +45,15 @@ import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.util.PythonUtils; 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.Idempotent; 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.nodes.Node; import com.oracle.truffle.api.object.DynamicObject; -import com.oracle.truffle.api.object.Location; -import com.oracle.truffle.api.object.Property; -import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; /** @@ -64,7 +61,7 @@ * object has dict, also bypasses any other additional logic in {@link ReadAttributeFromObjectNode}. */ @GenerateUncached -@GenerateInline(false) // footprint reduction 44 -> 25 +@GenerateInline public abstract class ReadAttributeFromPythonObjectNode extends PNodeWithContext { @NeverDefault public static ReadAttributeFromPythonObjectNode create() { @@ -76,87 +73,65 @@ public static ReadAttributeFromPythonObjectNode getUncached() { } public static Object executeUncached(PythonObject object, TruffleString key, Object defaultValue) { - return getUncached().execute(object, key, defaultValue); + return getUncached().execute(getUncached(), object, key, defaultValue); } - public abstract Object execute(PythonObject object, TruffleString key, Object defaultValue); + public final Object execute(PythonObject object, TruffleString key, Object defaultValue) { + return execute(this, object, key, defaultValue); + } public final Object execute(PythonObject object, TruffleString key) { - return execute(object, key, PNone.NO_VALUE); + return execute(this, object, key, PNone.NO_VALUE); } // used only by DynamicObjectStorage, which will be removed during the transition from // DynamicObject to ObjectHashMap - public abstract Object execute(DynamicObject object, TruffleString key, Object defaultValue); - - protected static Object getAttribute(DynamicObject object, TruffleString key, Object defaultValue) { - return DynamicObject.GetNode.getUncached().execute(object, key, defaultValue); + public final Object execute(DynamicObject object, TruffleString key, Object defaultValue) { + return execute(this, object, key, defaultValue); } - @Idempotent - protected static boolean isLongLivedObject(DynamicObject object) { - return object instanceof PythonModule || object instanceof PythonManagedClass; - } + public abstract Object execute(Node inliningTarget, PythonObject object, TruffleString key, Object defaultValue); - @Idempotent - protected static boolean isPrimitive(Object value) { - return PythonUtils.isPrimitive(value); + public final Object execute(Node inliningTarget, PythonObject object, TruffleString key) { + return execute(inliningTarget, object, key, PNone.NO_VALUE); } - @NonIdempotent - protected static boolean locationIsAssumedFinal(Location loc) { - return loc != null && loc.isAssumedFinal(); - } + public abstract Object execute(Node inliningTarget, DynamicObject object, TruffleString key, Object defaultValue); - protected static Location getLocationOrNull(Property prop) { - return prop == null ? null : prop.getLocation(); + @Specialization + protected static Object read(Node inliningTarget, DynamicObject dynamicObject, TruffleString key, Object defaultValue, + @Cached ReceiverCast receiverCast, + @Cached DynamicObject.GetNode getNode) { + return getNode.execute(receiverCast.execute(inliningTarget, dynamicObject), key, defaultValue); } - @SuppressWarnings("unused") - @Specialization(limit = "1", // - guards = { - "isSingleContext()", - "dynamicObject == cachedObject", - "isLongLivedObject(cachedObject)", - "key == cachedKey", - "dynamicObject.getShape() == cachedShape", - "locationIsAssumedFinal(loc)", - "!isPrimitive(value)" - }, // - assumptions = {"cachedShape.getValidAssumption()", "loc.getFinalAssumption()"}) - protected static Object readFinalAttr(DynamicObject dynamicObject, TruffleString key, Object defaultValue, - @Cached("key") TruffleString cachedKey, - @Cached(value = "dynamicObject", weak = true) DynamicObject cachedObject, - @Cached("dynamicObject.getShape()") Shape cachedShape, - @Cached("getLocationOrNull(cachedShape.getProperty(cachedKey))") Location loc, - @Cached(value = "getAttribute(dynamicObject, key, defaultValue)", weak = true) Object value) { - return value; - } + @GenerateCached(false) + @GenerateUncached + @GenerateInline + protected abstract static class ReceiverCast extends PNodeWithContext { - @SuppressWarnings("unused") - @Specialization(limit = "1", // - guards = { - "isSingleContext()", - "dynamicObject == cachedObject", - "isLongLivedObject(cachedObject)", - "key == cachedKey", - "dynamicObject.getShape() == cachedShape", - "locationIsAssumedFinal(loc)", - "isPrimitive(value)" - }, // - assumptions = {"cachedShape.getValidAssumption()", "loc.getFinalAssumption()"}) - protected static Object readFinalPrimitiveAttr(DynamicObject dynamicObject, TruffleString key, Object defaultValue, - @Cached("key") TruffleString cachedKey, - @Cached(value = "dynamicObject", weak = true) DynamicObject cachedObject, - @Cached("dynamicObject.getShape()") Shape cachedShape, - @Cached("getLocationOrNull(cachedShape.getProperty(cachedKey))") Location loc, - @Cached(value = "getAttribute(dynamicObject, key, defaultValue)") Object value) { - return value; - } + protected abstract DynamicObject execute(Node inliningTarget, DynamicObject object); - @Specialization(replaces = {"readFinalAttr", "readFinalPrimitiveAttr"}) - protected static Object readDirect(DynamicObject dynamicObject, TruffleString key, Object defaultValue, - @Cached DynamicObject.GetNode getNode) { - return getNode.execute(dynamicObject, key, defaultValue); + @Idempotent + protected static boolean isLongLivedObject(DynamicObject object) { + return object instanceof PythonModule || object instanceof PythonManagedClass; + } + + @SuppressWarnings("unused") + @Specialization(limit = "1", // + guards = { + "isSingleContext()", + "dynamicObject == cachedObject", + "isLongLivedObject(cachedObject)", + }) + protected static DynamicObject readFinalAttr(DynamicObject dynamicObject, + @Cached(value = "dynamicObject", weak = true) DynamicObject cachedObject) { + return cachedObject; + } + + @Specialization(replaces = {"readFinalAttr"}) + protected static DynamicObject readDirect(DynamicObject dynamicObject) { + return dynamicObject; + } } } 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 9ab937a7e4..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; @@ -103,6 +104,7 @@ static boolean writeToDynamicStorageNoTypeGuard(PythonObject obj, GetDictIfExist static boolean writeToDynamicStorageNoType(PythonObject object, TruffleString key, Object value, @SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict, @Cached WriteAttributeToPythonObjectNode writeNode) { + assert object.checkDictFlags(null); // Objects w/o dict that are not classes do not have any special handling writeNode.execute(object, key, value); return true; @@ -137,6 +139,7 @@ static boolean writeToDynamicStoragePythonClass(PythonClass klass, TruffleString @Exclusive @Cached InlinedBranchProfile updateFlags, @Cached DynamicObject.PutNode putNode, @Cached DynamicObject.SetShapeFlagsNode setShapeFlagsNode) { + assert klass.checkDictFlags(null); if (value == PNone.NO_VALUE) { updateFlags.enter(inliningTarget); setShapeFlagsNode.executeAdd(klass, HAS_NO_VALUE_PROPERTIES); @@ -162,6 +165,7 @@ static boolean writeToDictNoType(@SuppressWarnings("unused") PythonObject object @Bind("getDict.execute(object)") PDict dict, @Shared("updateStorage") @Cached InlinedBranchProfile updateStorage, @Shared("setHashingStorageItem") @Cached HashingStorageSetItem setHashingStorageItem) { + assert object.checkDictFlags(dict); return writeToDict(dict, key, value, inliningTarget, updateStorage, setHashingStorageItem); } @@ -172,6 +176,7 @@ static boolean writeToDictClass(PythonClass klass, TruffleString key, Object val @Bind("getDict.execute(klass)") PDict dict, @Shared("updateStorage") @Cached InlinedBranchProfile updateStorage, @Shared("setHashingStorageItem") @Cached HashingStorageSetItem setHashingStorageItem) { + assert klass.checkDictFlags(dict); return writeToDictManagedClass(klass, dict, key, value, inliningTarget, updateStorage, setHashingStorageItem); } @@ -182,6 +187,7 @@ static boolean deleteFromPythonObject(PythonObject obj, TruffleString key, Objec @SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict, @Bind("getDict.execute(obj)") PDict dict, @Cached HashingStorageNodes.HashingStorageDelItem hashingStorageDelItem) { + assert obj.checkDictFlags(dict); try { HashingStorage dictStorage = dict.getDictStorage(); return hashingStorageDelItem.execute(inliningTarget, dictStorage, key, dict); @@ -219,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); } @@ -232,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, @@ -242,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/FunctionNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/FunctionNodes.java index 1e6df8ffd6..247565b44c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/FunctionNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/FunctionNodes.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 @@ -44,22 +44,18 @@ import com.oracle.graal.python.builtins.objects.code.PCode; 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; import com.oracle.graal.python.builtins.objects.function.Signature; import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod; import com.oracle.graal.python.builtins.objects.method.PMethod; import com.oracle.graal.python.builtins.objects.method.PMethodBase; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.nodes.builtins.FunctionNodesFactory.GetCallTargetNodeGen; import com.oracle.graal.python.nodes.builtins.FunctionNodesFactory.GetSignatureNodeGen; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; -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.Shared; -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; @@ -129,66 +125,6 @@ static Object[] doBuiltinMethod(PBuiltinMethod builtinMethod) { } - @ImportStatic(PGuards.class) - @GenerateUncached - @GenerateInline - @GenerateCached(false) - public abstract static class GetKeywordDefaultsNode extends PNodeWithContext { - - public abstract PKeyword[] execute(Node inliningTarget, Object function); - - public abstract PKeyword[] execute(Node inliningTarget, PMethodBase method); - - /** - * Fast-path if method is a partial evaluation constant. - */ - public static PKeyword[] getMethodKeywords(PMethodBase method) { - CompilerAsserts.partialEvaluationConstant(method); - return getFunctionKeywords(method.getFunction()); - } - - /** - * Fast-path if the function is a partial evaluation constant. - */ - public static PKeyword[] getFunctionKeywords(Object fun) { - CompilerAsserts.partialEvaluationConstant(fun); - if (fun instanceof PFunction f) { - return f.getKwDefaults(); - } else if (fun instanceof PBuiltinFunction f) { - return f.getKwDefaults(); - } - throw CompilerDirectives.shouldNotReachHere(); - } - - @Specialization - static PKeyword[] doFunction(PFunction function) { - return function.getKwDefaults(); - } - - @Specialization - static PKeyword[] doBuiltinFunction(PBuiltinFunction builtinFunction) { - return builtinFunction.getKwDefaults(); - } - - @Specialization(guards = "isPFunction(function)") - static PKeyword[] doMethod(@SuppressWarnings("unused") PMethod method, - @Bind("method.getFunction()") Object function) { - return ((PFunction) function).getKwDefaults(); - } - - @Specialization(guards = "isPBuiltinFunction(method.getFunction())") - static PKeyword[] doMethodBuiltin(@SuppressWarnings("unused") PMethod method, - @Bind("method.getFunction()") Object function) { - return ((PBuiltinFunction) function).getKwDefaults(); - } - - @Specialization - static PKeyword[] doBuiltinMethod(PBuiltinMethod builtinMethod) { - return builtinMethod.getBuiltinFunction().getKwDefaults(); - } - - } - @GenerateUncached @GenerateInline @GenerateCached(false) @@ -278,54 +214,4 @@ public static GetSignatureNode getUncached() { return GetSignatureNodeGen.getUncached(); } } - - @ImportStatic(PGuards.class) - @GenerateUncached - @GenerateInline(false) // used lazily - public abstract static class GetCallTargetNode extends PNodeWithContext { - - public abstract RootCallTarget execute(Object function); - - @Specialization - static RootCallTarget doFunction(PFunction function, - @Bind Node inliningTarget, - @Shared("getCode") @Cached GetFunctionCodeNode getFunctionCodeNode, - @Shared("getCt") @Cached CodeNodes.GetCodeCallTargetNode getCt) { - return getCt.execute(inliningTarget, getFunctionCodeNode.execute(inliningTarget, function)); - } - - @Specialization - static RootCallTarget doBuiltinFunction(PBuiltinFunction builtinFunction) { - return builtinFunction.getCallTarget(); - } - - @Specialization(guards = "isPFunction(function)") - static RootCallTarget doMethod(@SuppressWarnings("unused") PMethod method, - @Bind Node inliningTarget, - @Bind("method.getFunction()") Object function, - @Shared("getCode") @Cached GetFunctionCodeNode getFunctionCodeNode, - @Shared("getCt") @Cached CodeNodes.GetCodeCallTargetNode getCt) { - return getCt.execute(inliningTarget, getFunctionCodeNode.execute(inliningTarget, (PFunction) function)); - } - - @Specialization(guards = "isPBuiltinFunction(method.getFunction())") - static RootCallTarget doMethod(@SuppressWarnings("unused") PMethod method, - @Bind("method.getFunction()") Object function) { - return ((PBuiltinFunction) function).getCallTarget(); - } - - @Specialization - static RootCallTarget doBuiltinMethod(PBuiltinMethod builtinMethod) { - return builtinMethod.getBuiltinFunction().getCallTarget(); - } - - @Fallback - static RootCallTarget fallback(@SuppressWarnings("unused") Object callable) { - return null; - } - - public static GetCallTargetNode getUncached() { - return GetCallTargetNodeGen.getUncached(); - } - } } 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 6b30c1beeb..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; @@ -269,7 +270,7 @@ protected PList doGeneric(VirtualFrame frame, Object value, */ @GenerateUncached @GenerateInline(false) // footprint reduction 36 -> 17 - @OperationProxy.Proxyable(storeBytecodeIndex = false) + @OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract static class AppendNode extends PNodeWithContext { private static final BranchProfile[] DISABLED = new BranchProfile[]{BranchProfile.getUncached()}; @@ -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 7c4b1893d3..16ac0db8d9 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 @@ -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,15 +40,17 @@ */ 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; +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.tuple.PTuple; import com.oracle.graal.python.lib.PyObjectGetIter; @@ -96,15 +98,27 @@ static PTuple list(PList iterable, return PFactory.createTuple(language, copyNode.execute(inliningTarget, iterable.getSequenceStorage())); } + @Specialization(guards = "tupleCheck.execute(inliningTarget, iterable)", limit = "1") + static PTuple nativeTuple(PythonAbstractNativeObject iterable, + @Bind Node inliningTarget, + @Bind PythonLanguage language, + @SuppressWarnings("unused") @Cached PyTupleCheckNode tupleCheck, + @Cached GetNativeTupleStorage getNativeTupleStorage, + @Cached SequenceStorageNodes.ToArrayNode toArrayNode) { + return PFactory.createTuple(language, toArrayNode.execute(inliningTarget, getNativeTupleStorage.execute(iterable))); + } + @Fallback @InliningCutoff static PTuple generic(VirtualFrame frame, Object iterable, @Bind Node inliningTarget, @Bind PythonLanguage language, + @Cached IteratorNodes.GetLength lenNode, @Cached CreateStorageFromIteratorNode storageNode, @Cached PyObjectGetIter getIter) { + int len = lenNode.execute(frame, inliningTarget, iterable); Object iterObj = getIter.execute(frame, inliningTarget, iterable); - return PFactory.createTuple(language, storageNode.execute(frame, iterObj)); + return PFactory.createTuple(language, storageNode.execute(frame, iterObj, len)); } @NeverDefault @@ -140,13 +154,16 @@ SequenceStorage getNative(PythonAbstractNativeObject tuple, public abstract static class GetNativeTupleStorage extends Node { public abstract NativeObjectSequenceStorage execute(PythonAbstractNativeObject tuple); + public static GetNativeTupleStorage getUncached() { + return TupleNodesFactory.GetNativeTupleStorageNodeGen.getUncached(); + } + @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/bytecode/CopyDictWithoutKeysNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/CopyDictWithoutKeysNode.java index 4d1e27f40c..d0bd140a9b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/CopyDictWithoutKeysNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/CopyDictWithoutKeysNode.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 @@ -52,6 +52,7 @@ 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.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; @@ -60,11 +61,12 @@ import com.oracle.truffle.api.nodes.Node; @GenerateInline(false) // used in BCI root node -@OperationProxy.Proxyable(storeBytecodeIndex = true) +@OperationProxy.Proxyable(storeBytecodeIndex = true, allowUncached = true) +@GenerateUncached public abstract class CopyDictWithoutKeysNode extends PNodeWithContext { public abstract PDict execute(Frame frame, Object subject, Object[] keys); - @Specialization(guards = {"keys.length == keysLength", "keysLength <= 32"}, limit = "1") + @Specialization(guards = {"keys.length == keysLength", "keysLength <= 32"}, limit = "1", excludeForUncached = true) public static PDict copy(VirtualFrame frame, Object subject, @NeverDefault @SuppressWarnings("unused") Object[] keys, @Bind Node inliningTarget, @Cached("keys.length") int keysLength, @@ -85,7 +87,7 @@ private static void deleteKeys(VirtualFrame frame, Node inliningTarget, Object[] } } - @Specialization(guards = "keys.length > 32") + @Specialization public static PDict copy(VirtualFrame frame, Object subject, Object[] keys, @Bind Node inliningTarget, @Bind PythonLanguage language, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetAIterNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetAIterNode.java index 172778a738..b184976f38 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetAIterNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetAIterNode.java @@ -62,7 +62,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -@Proxyable(storeBytecodeIndex = true) +@Proxyable(storeBytecodeIndex = true, allowUncached = true) @GenerateUncached @GenerateInline(false) // used in bytecode root node public abstract class GetAIterNode extends PNodeWithContext { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetANextNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetANextNode.java index 543b42bd08..5e1174e8f3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetANextNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetANextNode.java @@ -61,7 +61,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -@Proxyable(storeBytecodeIndex = true) +@Proxyable(storeBytecodeIndex = true, allowUncached = true) @GenerateUncached @GenerateInline(false) // used in bytecode root node public abstract class GetANextNode extends PNodeWithContext { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetSendValueNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetSendValueNode.java index a9149f87ef..f72b67ddbd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetSendValueNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetSendValueNode.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 @@ -62,7 +62,7 @@ Object doNext(@SuppressWarnings("unused") Object specialArgument) { @Specialization Object doThrow(ThrowData throwData) { - throw PException.fromObject(throwData.pythonException, this, throwData.withJavaStacktrace); + throw PException.fromObjectFixUncachedLocation(throwData.pythonException, this, throwData.withJavaStacktrace); } @Fallback diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetYieldFromIterNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetYieldFromIterNode.java index 7e567ec27e..2f13ddd4c9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetYieldFromIterNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/GetYieldFromIterNode.java @@ -55,7 +55,7 @@ @GenerateUncached @GenerateInline(false) // used in BCI root node -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) public abstract class GetYieldFromIterNode extends Node { public abstract Object execute(VirtualFrame frame, Object receiver); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.java index 47ebd89cad..6c1ed0cb3f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MakeFunctionNode.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,40 +45,37 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.objects.cell.PCell; import com.oracle.graal.python.builtins.objects.code.PCode; +import com.oracle.graal.python.builtins.objects.function.PArguments; import com.oracle.graal.python.builtins.objects.function.PFunction; 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.object.PythonObject; import com.oracle.graal.python.compiler.BytecodeCodeUnit; import com.oracle.graal.python.compiler.OpCodes; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.graal.python.util.LazySource; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; public abstract class MakeFunctionNode extends PNodeWithContext { - private final RootCallTarget callTarget; - private final BytecodeCodeUnit code; - private final Signature signature; + private final int codeIndex; + private final BytecodeCodeUnit codeUnit; @CompilationFinal private PCode cachedCode; private final Assumption codeStableAssumption = Truffle.getRuntime().createAssumption("code stable assumption"); public abstract int execute(VirtualFrame frame, Object globals, int initialStackTop, int flags); - public MakeFunctionNode(RootCallTarget callTarget, BytecodeCodeUnit code, Signature signature) { - this.callTarget = callTarget; - this.code = code; - this.signature = signature; + public MakeFunctionNode(int codeIndex, BytecodeCodeUnit codeUnit) { + this.codeIndex = codeIndex; + this.codeUnit = codeUnit; } @Specialization @@ -87,18 +84,18 @@ int makeFunction(VirtualFrame frame, Object globals, int initialStackTop, int fl @Cached WriteAttributeToPythonObjectNode writeAttrNode) { int stackTop = initialStackTop; - PCode codeObj = cachedCode; - if (codeObj == null) { + PCode code = cachedCode; + if (code == null) { + code = PArguments.getCodeObject(frame).getOrCreateChildCode(codeIndex, codeUnit); if (PythonLanguage.get(this).isSingleContext()) { CompilerDirectives.transferToInterpreterAndInvalidate(); /* * We cannot initialize the cached code in create, because that may be called * without langauge context when materializing nodes for instrumentation */ - cachedCode = codeObj = PFactory.createCode(language, callTarget, signature, code); + cachedCode = code; } else { // In multi-context mode we have to create the code for every execution - codeObj = PFactory.createCode(language, callTarget, signature, code); } } @@ -124,7 +121,7 @@ int makeFunction(VirtualFrame frame, Object globals, int initialStackTop, int fl frame.setObject(stackTop--, null); } - PFunction function = PFactory.createFunction(language, code.name, code.qualname, codeObj, (PythonObject) globals, defaults, kwdefaults, closure, codeStableAssumption); + PFunction function = PFactory.createFunction(language, codeUnit.name, codeUnit.qualname, code, (PythonObject) globals, defaults, kwdefaults, closure, codeStableAssumption); if (annotations != null) { writeAttrNode.execute(function, T___ANNOTATIONS__, annotations); @@ -134,19 +131,8 @@ int makeFunction(VirtualFrame frame, Object globals, int initialStackTop, int fl return stackTop; } - public static MakeFunctionNode create(PythonLanguage language, BytecodeCodeUnit code, LazySource lazySource, boolean internal) { - RootCallTarget callTarget; - PBytecodeRootNode bytecodeRootNode = PBytecodeRootNode.create(language, code, lazySource, internal); - if (code.isGeneratorOrCoroutine()) { - // TODO what should the frameDescriptor be? does it matter? - callTarget = new PBytecodeGeneratorFunctionRootNode(language, bytecodeRootNode.getFrameDescriptor(), bytecodeRootNode, code.name).getCallTarget(); - } else { - callTarget = bytecodeRootNode.getCallTarget(); - } - return MakeFunctionNodeGen.create(callTarget, code, bytecodeRootNode.getSignature()); - } - - public RootCallTarget getCallTarget() { - return callTarget; + @NeverDefault + public static MakeFunctionNode create(int codeIndex, BytecodeCodeUnit codeUnit) { + return MakeFunctionNodeGen.create(codeIndex, codeUnit); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MatchClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MatchClassNode.java index adb0911187..46b8bf492e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MatchClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MatchClassNode.java @@ -43,27 +43,28 @@ import static com.oracle.graal.python.builtins.objects.type.TypeFlags.MATCH_SELF; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___MATCH_ARGS; import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions; -import com.oracle.graal.python.builtins.objects.str.StringBuiltins; -import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins; import com.oracle.graal.python.builtins.objects.type.TypeNodes; -import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.lib.PyObjectGetAttrO; +import com.oracle.graal.python.lib.PyObjectIsInstanceNode; import com.oracle.graal.python.lib.PyTupleCheckExactNode; +import com.oracle.graal.python.lib.PyTupleGetItem; import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; +import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; 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.dsl.GenerateInline; +import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; @@ -73,23 +74,25 @@ import com.oracle.truffle.api.strings.TruffleString; @GenerateInline(false) // used in BCI root node +@GenerateUncached public abstract class MatchClassNode extends PNodeWithContext { public abstract Object execute(Frame frame, Object subject, Object type, int nargs, TruffleString[] kwArgs); @Specialization - Object match(VirtualFrame frame, Object subject, Object type, int nargs, @NeverDefault @SuppressWarnings("unused") TruffleString[] kwArgsArg, + static Object match(VirtualFrame frame, Object subject, Object type, int nargs, @NeverDefault @SuppressWarnings("unused") TruffleString[] kwArgsArg, @Bind Node inliningTarget, @Cached(value = "kwArgsArg", dimensions = 1) TruffleString[] kwArgs, @Cached TypeNodes.IsTypeNode isTypeNode, - @Cached BuiltinFunctions.IsInstanceNode isInstanceNode, + @Cached PyObjectIsInstanceNode isInstanceNode, @Cached PyObjectGetAttrO getAttr, @Cached TypeNodes.GetTypeFlagsNode getTypeFlagsNode, @Cached IsBuiltinObjectProfile isClassProfile, - @Cached StringBuiltins.StringRichCmpNode eqStrNode, + @Cached CastToTruffleStringNode castStringNode, + @Cached TruffleString.EqualNode equalNode, @Cached PyTupleCheckExactNode tupleCheckExactNode, @Bind PythonLanguage language, @Cached PyTupleSizeNode tupleSizeNode, - @Cached TupleBuiltins.GetItemNode getItemNode, + @Cached PyTupleGetItem getItemNode, @Cached PyUnicodeCheckNode unicodeCheckNode, @Cached PRaiseNode raise) { @@ -97,7 +100,7 @@ Object match(VirtualFrame frame, Object subject, Object type, int nargs, @NeverD throw raise.raise(inliningTarget, TypeError, ErrorMessages.CALLED_MATCH_PAT_MUST_BE_TYPE); } - if (!isInstanceNode.executeWith(frame, subject, type)) { + if (!isInstanceNode.execute(frame, subject, type)) { return null; } @@ -133,14 +136,14 @@ Object match(VirtualFrame frame, Object subject, Object type, int nargs, @NeverD attrs[attrsLength[0]++] = subject; } else { attrs = new Object[nargs + kwArgs.length]; - getArgs(frame, inliningTarget, subject, type, nargs, seen, seenLength, attrs, attrsLength, matchArgs, getAttr, eqStrNode, getItemNode, unicodeCheckNode, raise); + getArgs(frame, inliningTarget, subject, type, nargs, seen, seenLength, attrs, attrsLength, matchArgs, getAttr, castStringNode, equalNode, getItemNode, unicodeCheckNode, raise); } } else { attrs = new Object[kwArgs.length]; } // Finally, the keyword subpatterns: try { - getKwArgs(frame, inliningTarget, subject, type, kwArgs, seen, seenLength, attrs, attrsLength, getAttr, eqStrNode, raise); + getKwArgs(frame, inliningTarget, subject, type, kwArgs, seen, seenLength, attrs, attrsLength, getAttr, castStringNode, equalNode, raise); } catch (PException pe) { // missing keyword argument will throw AttributeError, but in pattern matching, that // should be ignored @@ -153,42 +156,43 @@ Object match(VirtualFrame frame, Object subject, Object type, int nargs, @NeverD @ExplodeLoop private static void getArgs(VirtualFrame frame, Node inliningTarget, Object subject, Object type, int nargs, Object[] seen, int[] seenLength, Object[] attrs, int[] attrsLength, Object matchArgs, PyObjectGetAttrO getAttr, - StringBuiltins.StringRichCmpNode eqStrNode, TupleBuiltins.GetItemNode getItemNode, PyUnicodeCheckNode unicodeCheckNode, PRaiseNode raise) { + CastToTruffleStringNode castStringNode, TruffleString.EqualNode equalNode, PyTupleGetItem getItemNode, PyUnicodeCheckNode unicodeCheckNode, PRaiseNode raise) { CompilerAsserts.partialEvaluationConstant(nargs); for (int i = 0; i < nargs; i++) { - Object name = getItemNode.execute(frame, matchArgs, i); + Object name = getItemNode.execute(inliningTarget, matchArgs, i); if (!unicodeCheckNode.execute(inliningTarget, name)) { throw raise.raise(inliningTarget, TypeError, ErrorMessages.MATCH_ARGS_ELEMENTS_MUST_BE_STRINGS_GOT_P, name); } - setName(frame, inliningTarget, type, name, seen, seenLength, eqStrNode, raise); + setName(inliningTarget, type, name, seen, seenLength, castStringNode, equalNode, raise); attrs[attrsLength[0]++] = getAttr.execute(frame, inliningTarget, subject, name); } } @ExplodeLoop private static void getKwArgs(VirtualFrame frame, Node inliningTarget, Object subject, Object type, TruffleString[] kwArgs, Object[] seen, int[] seenLength, Object[] attrs, int[] attrsLength, - PyObjectGetAttrO getAttr, - StringBuiltins.StringRichCmpNode eqStrNode, PRaiseNode raise) { + PyObjectGetAttrO getAttr, CastToTruffleStringNode castStringNode, TruffleString.EqualNode equalNode, PRaiseNode raise) { CompilerAsserts.partialEvaluationConstant(kwArgs); for (int i = 0; i < kwArgs.length; i++) { TruffleString name = kwArgs[i]; CompilerAsserts.partialEvaluationConstant(name); - setName(frame, inliningTarget, type, name, seen, seenLength, eqStrNode, raise); + setName(inliningTarget, type, name, seen, seenLength, castStringNode, equalNode, raise); attrs[attrsLength[0]++] = getAttr.execute(frame, inliningTarget, subject, name); } } - private static void setName(VirtualFrame frame, Node inliningTarget, Object type, Object name, Object[] seen, int[] seenLength, StringBuiltins.StringRichCmpNode eqNode, PRaiseNode raise) { - if (seenLength[0] > 0 && contains(frame, seen, name, eqNode)) { + private static void setName(Node inliningTarget, Object type, Object name, Object[] seen, int[] seenLength, CastToTruffleStringNode castStringNode, TruffleString.EqualNode equalNode, + PRaiseNode raise) { + if (seenLength[0] > 0 && contains(inliningTarget, seen, name, castStringNode, equalNode)) { throw raise.raise(inliningTarget, TypeError, ErrorMessages.S_GOT_MULTIPLE_SUBPATTERNS_FOR_ATTR_S, type, name); } seen[seenLength[0]++] = name; } @ExplodeLoop - private static boolean contains(VirtualFrame frame, Object[] seen, Object name, StringBuiltins.StringRichCmpNode eqNode) { + private static boolean contains(Node inliningTarget, Object[] seen, Object name, CastToTruffleStringNode castStringNode, TruffleString.EqualNode equalNode) { + TruffleString nameString = castStringNode.castKnownString(inliningTarget, name); for (int i = 0; i < seen.length; i++) { - if (seen[i] != null && (boolean) eqNode.execute(frame, seen[i], name, RichCmpOp.Py_EQ)) { + if (seen[i] != null && equalNode.execute(castStringNode.castKnownString(inliningTarget, seen[i]), nameString, TS_ENCODING)) { return true; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MatchKeysNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MatchKeysNode.java index 08a68f1516..15469c5a00 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MatchKeysNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/MatchKeysNode.java @@ -57,6 +57,7 @@ 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.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; @@ -65,6 +66,7 @@ import com.oracle.truffle.api.nodes.Node; @GenerateInline(false) // Used in BCI +@GenerateUncached public abstract class MatchKeysNode extends PNodeWithContext { public abstract Object execute(Frame frame, Object map, Object[] keys); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java index e86ab84f0c..af53f81826 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/PBytecodeRootNode.java @@ -61,8 +61,6 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions.FormatNode; -import com.oracle.graal.python.builtins.modules.BuiltinFunctionsFactory.FormatNodeFactory.FormatNodeGen; import com.oracle.graal.python.builtins.modules.MarshalModuleBuiltins; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.asyncio.GetAwaitableNode; @@ -143,10 +141,12 @@ import com.oracle.graal.python.lib.PyNumberSubtractNode; import com.oracle.graal.python.lib.PyNumberTrueDivideNode; import com.oracle.graal.python.lib.PyNumberXorNode; -import com.oracle.graal.python.lib.PyObjectAsciiNode; -import com.oracle.graal.python.lib.PyObjectAsciiNodeGen; +import com.oracle.graal.python.lib.PyObjectAsciiAsObjectNode; +import com.oracle.graal.python.lib.PyObjectAsciiAsObjectNodeGen; import com.oracle.graal.python.lib.PyObjectDelItem; import com.oracle.graal.python.lib.PyObjectDelItemNodeGen; +import com.oracle.graal.python.lib.PyObjectFormat; +import com.oracle.graal.python.lib.PyObjectFormatNodeGen; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectGetAttrNodeGen; import com.oracle.graal.python.lib.PyObjectGetItem; @@ -237,7 +237,6 @@ import com.oracle.graal.python.runtime.sequence.storage.LongSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; -import com.oracle.graal.python.util.LazySource; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; @@ -401,9 +400,10 @@ public final class PBytecodeRootNode extends PRootNode implements BytecodeOSRNod private static final NodeSupplier NODE_STR = PyObjectStrAsObjectNode::create; private static final PyObjectReprAsObjectNode UNCACHED_REPR = PyObjectReprAsObjectNode.getUncached(); private static final NodeSupplier NODE_REPR = PyObjectReprAsObjectNode::create; - private static final PyObjectAsciiNode UNCACHED_ASCII = PyObjectAsciiNode.getUncached(); - private static final NodeSupplier NODE_ASCII = PyObjectAsciiNode::create; - private static final NodeSupplier NODE_FORMAT = FormatNode::create; + private static final PyObjectAsciiAsObjectNode UNCACHED_ASCII = PyObjectAsciiAsObjectNode.getUncached(); + private static final NodeSupplier NODE_ASCII = PyObjectAsciiAsObjectNode::create; + private static final PyObjectFormat UNCACHED_FORMAT = PyObjectFormat.getUncached(); + private static final NodeSupplier NODE_FORMAT = PyObjectFormat::create; private static final NodeSupplier NODE_SEND = SendNode::create; private static final NodeSupplier NODE_THROW = ThrowNode::create; private static final WriteGlobalNode UNCACHED_WRITE_GLOBAL = WriteGlobalNode.getUncached(); @@ -549,7 +549,7 @@ public final class PBytecodeRootNode extends PRootNode implements BytecodeOSRNod final int classcellIndex; private final BytecodeCodeUnit co; - private final LazySource lazySource; + private final Source source; private SourceSection sourceSection; // For deferred deprecation warnings private final ParserCallbacksImpl parserCallbacks; @@ -663,15 +663,25 @@ private static FrameDescriptor makeFrameDescriptor(BytecodeCodeUnit co, FrameInf } @TruffleBoundary - public static PBytecodeRootNode create(PythonLanguage language, BytecodeCodeUnit co, LazySource lazySource, boolean internal) { - return create(language, co, lazySource, internal, null); + public static PBytecodeRootNode create(PythonLanguage language, BytecodeCodeUnit co, Source source, boolean internal) { + return create(language, co, source, internal, null); } @TruffleBoundary - public static PBytecodeRootNode create(PythonLanguage language, BytecodeCodeUnit co, LazySource lazySource, boolean internal, ParserCallbacksImpl parserCallbacks) { + public static RootNode createMaybeGenerator(PythonLanguage language, BytecodeCodeUnit co, Source source, boolean internal) { + PBytecodeRootNode bytecodeRootNode = PBytecodeRootNode.create(language, co, source, internal); + if (co.isGeneratorOrCoroutine()) { + return new PBytecodeGeneratorFunctionRootNode(language, bytecodeRootNode.getFrameDescriptor(), bytecodeRootNode, co.name); + } else { + return bytecodeRootNode; + } + } + + @TruffleBoundary + public static PBytecodeRootNode create(PythonLanguage language, BytecodeCodeUnit co, Source source, boolean internal, ParserCallbacksImpl parserCallbacks) { BytecodeFrameInfo frameInfo = new BytecodeFrameInfo(); FrameDescriptor fd = makeFrameDescriptor(co, frameInfo); - PBytecodeRootNode rootNode = new PBytecodeRootNode(language, fd, co.computeSignature(), co, lazySource, internal, parserCallbacks); + PBytecodeRootNode rootNode = new PBytecodeRootNode(language, fd, co.computeSignature(), co, source, internal, parserCallbacks); PythonContext context = PythonContext.get(rootNode); if (context != null && context.getOption(PythonOptions.EagerlyMaterializeInstrumentationNodes)) { rootNode.adoptChildren(); @@ -682,14 +692,14 @@ public static PBytecodeRootNode create(PythonLanguage language, BytecodeCodeUnit } @TruffleBoundary - private PBytecodeRootNode(PythonLanguage language, FrameDescriptor fd, Signature sign, BytecodeCodeUnit co, LazySource source, boolean internal, + private PBytecodeRootNode(PythonLanguage language, FrameDescriptor fd, Signature sign, BytecodeCodeUnit co, Source source, boolean internal, ParserCallbacksImpl parserCallbacks) { super(language, fd); this.celloffset = co.varnames.length; this.freeoffset = celloffset + co.cellvars.length; this.stackoffset = freeoffset + co.freevars.length; this.bcioffset = stackoffset + co.stacksize; - this.lazySource = source; + this.source = source; this.internal = internal; this.parserCallbacks = parserCallbacks; this.signature = sign; @@ -777,12 +787,12 @@ public BytecodeCodeUnit getCodeUnit() { return co; } - public Source getSource() { - return lazySource.getSource(); + public Source getSourceWithCharacters() { + return getLanguage().getOrCreateSourceWithContent(source); } - public LazySource getLazySource() { - return lazySource; + public Source getSource() { + return source; } public byte[] getBytecode() { @@ -895,7 +905,7 @@ private void doInsertChildNode(Node[] nodes, int nodeIndex, Node newNode) { } } - private PythonLanguage getLanguage() { + public PythonLanguage getLanguage() { return getLanguage(PythonLanguage.class); } @@ -2172,7 +2182,7 @@ private Object bytecodeLoop(VirtualFrame virtualFrame, Frame localFrame, Bytecod case OpCodesConstants.MAKE_FUNCTION: { oparg |= Byte.toUnsignedInt(localBC[++bci]); int flags = Byte.toUnsignedInt(localBC[++bci]); - stackTop = bytecodeMakeFunction(virtualFrame, globals, stackTop, localNodes, beginBci, flags, localConsts[oparg]); + stackTop = bytecodeMakeFunction(virtualFrame, globals, stackTop, localNodes, beginBci, oparg, flags, localConsts[oparg]); break; } case OpCodesConstants.SETUP_ANNOTATIONS: { @@ -2716,9 +2726,9 @@ private void bytecodeSetupAnnotations(VirtualFrame virtualFrame, boolean useCach } @BytecodeInterpreterSwitch - private int bytecodeMakeFunction(VirtualFrame virtualFrame, Object globals, int stackTop, Node[] localNodes, int beginBci, int flags, Object localConsts) { + private int bytecodeMakeFunction(VirtualFrame virtualFrame, Object globals, int stackTop, Node[] localNodes, int beginBci, int codeIndex, int flags, Object localConsts) { BytecodeCodeUnit codeUnit = (BytecodeCodeUnit) localConsts; - MakeFunctionNode makeFunctionNode = insertMakeFunctionNode(localNodes, beginBci, codeUnit); + MakeFunctionNode makeFunctionNode = insertMakeFunctionNode(localNodes, beginBci, codeIndex, codeUnit); return makeFunctionNode.execute(virtualFrame, globals, stackTop, flags); } @@ -2899,23 +2909,25 @@ private Object notifyEnter(VirtualFrame virtualFrame, InstrumentationSupport ins return null; } - private MakeFunctionNode insertMakeFunctionNode(Node[] localNodes, int beginBci, BytecodeCodeUnit codeUnit) { - return insertChildNode(localNodes, beginBci, MakeFunctionNodeGen.class, () -> MakeFunctionNode.create(getLanguage(PythonLanguage.class), codeUnit, lazySource, internal)); + private MakeFunctionNode insertMakeFunctionNode(Node[] localNodes, int beginBci, int codeIndex, BytecodeCodeUnit codeUnit) { + return insertChildNode(localNodes, beginBci, MakeFunctionNodeGen.class, () -> MakeFunctionNode.create(codeIndex, codeUnit)); } public void materializeContainedFunctionsForInstrumentation(Set> materializedTags) { usingCachedNodes = true; - BytecodeCodeUnit.iterateBytecode(bytecode, (bci, op, oparg, followingArgs) -> { - if (op == OpCodes.MAKE_FUNCTION) { - BytecodeCodeUnit codeUnit = (BytecodeCodeUnit) consts[oparg]; - MakeFunctionNode makeFunctionNode = insertMakeFunctionNode(getChildNodes(), bci, codeUnit); - RootNode rootNode = makeFunctionNode.getCallTarget().getRootNode(); + PythonLanguage language = getLanguage(); + for (int i = 0; i < co.constants.length; i++) { + if (co.constants[i] instanceof BytecodeCodeUnit codeUnit) { + RootNode rootNode = language.createCachedRootNode( + l -> PBytecodeRootNode.createMaybeGenerator(language, codeUnit, getSource(), isInternal()), + codeUnit); + rootNode.getCallTarget(); // make sure the calltarget is initialized if (rootNode instanceof PBytecodeGeneratorFunctionRootNode) { rootNode = ((PBytecodeGeneratorFunctionRootNode) rootNode).getBytecodeRootNode(); } ((PBytecodeRootNode) rootNode).instrumentationRoot.materializeInstrumentableNodes(materializedTags); } - }); + } } public Node createInstrumentationMaterializationForwarder() { @@ -3441,6 +3453,9 @@ private void chainPythonExceptions(VirtualFrame virtualFrame, MutableLoopData mu } private void chainPythonExceptions(PException current, PException context) { + if (current.isReraised()) { + return; + } if (chainExceptionsNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); chainExceptionsNode = insert(ChainExceptionsNode.create()); @@ -4989,12 +5004,12 @@ private int bytecodeFormatValue(VirtualFrame virtualFrame, int initialStackTop, value = insertChildNode(localNodes, bci, UNCACHED_REPR, PyObjectReprAsObjectNodeGen.class, NODE_REPR, useCachedNodes).executeCached(virtualFrame, value); break; case FormatOptions.FVC_ASCII: - value = insertChildNode(localNodes, bci, UNCACHED_ASCII, PyObjectAsciiNodeGen.class, NODE_ASCII, useCachedNodes).executeCached(virtualFrame, value); + value = insertChildNode(localNodes, bci, UNCACHED_ASCII, PyObjectAsciiAsObjectNodeGen.class, NODE_ASCII, useCachedNodes).executeCached(virtualFrame, value); break; default: assert type == FormatOptions.FVC_NONE; } - FormatNode formatNode = insertChildNode(localNodes, bci + 1, FormatNodeGen.class, NODE_FORMAT); + PyObjectFormat formatNode = insertChildNode(localNodes, bci + 1, UNCACHED_FORMAT, PyObjectFormatNodeGen.class, NODE_FORMAT, useCachedNodes); value = formatNode.execute(virtualFrame, value, spec); virtualFrame.setObject(stackTop, value); return stackTop; @@ -5389,9 +5404,7 @@ private void bytecodeLoadConstCollection(VirtualFrame virtualFrame, int stackTop } case CollectionBits.ELEMENT_OBJECT: { Object[] a = (Object[]) array; - if (list) { - a = PythonUtils.arrayCopyOf(a, a.length); - } + a = PythonUtils.arrayCopyOf(a, a.length); storage = new ObjectSequenceStorage(a); break; } @@ -5845,10 +5858,10 @@ static void doIt(VirtualFrame frame, ObjectHashMap map, Object key, Object value } @ExplodeLoop - private static ObjectHashMap moveFromStackToSetHashMap(VirtualFrame virtualFrame, int start, int stop, ObjHashMapPutNode putNode) { + private static EconomicMapStorage moveFromStackToSetHashMap(VirtualFrame virtualFrame, int start, int stop, ObjHashMapPutNode putNode) { CompilerAsserts.partialEvaluationConstant(start); CompilerAsserts.partialEvaluationConstant(stop); - var result = new ObjectHashMap(stop - start); + EconomicMapStorage result = EconomicMapStorage.create(stop - start); for (int i = start; i < stop; i++) { putNode.execute(virtualFrame, result, virtualFrame.getObject(i), PNone.NONE); virtualFrame.clear(i); @@ -5857,10 +5870,10 @@ private static ObjectHashMap moveFromStackToSetHashMap(VirtualFrame virtualFrame } @ExplodeLoop - private static ObjectHashMap moveFromStackToDictHashMap(VirtualFrame virtualFrame, int start, int stop, ObjHashMapPutNode putNode) { + private static EconomicMapStorage moveFromStackToDictHashMap(VirtualFrame virtualFrame, int start, int stop, ObjHashMapPutNode putNode) { CompilerAsserts.partialEvaluationConstant(start); CompilerAsserts.partialEvaluationConstant(stop); - var result = new ObjectHashMap((stop - start) / 2); + EconomicMapStorage result = EconomicMapStorage.create((stop - start) / 2); for (int i = start; i + 1 < stop; i += 2) { putNode.execute(virtualFrame, result, virtualFrame.getObject(i), virtualFrame.getObject(i + 1)); virtualFrame.clear(i); @@ -5889,16 +5902,16 @@ private int bytecodeCollectionFromStack(VirtualFrame virtualFrame, int type, int case CollectionBits.KIND_SET: { ObjHashMapPutNode putNode = insertChildNode(localNodes, nodeIndex, UNCACHED_OBJ_HASHMAP_PUT, ObjHashMapPutNodeGen.class, NODE_OBJ_HASHMAP_PUT, useCachedNodes); - ObjectHashMap storage = moveFromStackToSetHashMap(virtualFrame, stackTop - count + 1, stackTop + 1, putNode); - res = PFactory.createSet(getLanguage(), new EconomicMapStorage(storage, false)); + EconomicMapStorage storage = moveFromStackToSetHashMap(virtualFrame, stackTop - count + 1, stackTop + 1, putNode); + res = PFactory.createSet(getLanguage(), storage); break; } case CollectionBits.KIND_DICT: { ObjHashMapPutNode putNode = insertChildNode(localNodes, nodeIndex, UNCACHED_OBJ_HASHMAP_PUT, ObjHashMapPutNodeGen.class, NODE_OBJ_HASHMAP_PUT, useCachedNodes); assert count % 2 == 0; - ObjectHashMap storage = moveFromStackToDictHashMap(virtualFrame, stackTop - count + 1, stackTop + 1, putNode); - res = PFactory.createDict(getLanguage(), new EconomicMapStorage(storage, false)); + EconomicMapStorage storage = moveFromStackToDictHashMap(virtualFrame, stackTop - count + 1, stackTop + 1, putNode); + res = PFactory.createDict(getLanguage(), storage); break; } case CollectionBits.KIND_KWORDS: { @@ -6150,11 +6163,11 @@ public boolean frameIsVisibleToPython() { public SourceSection getSourceSection() { if (sourceSection != null) { return sourceSection; - } else if (!getSource().hasCharacters()) { - sourceSection = getSource().createUnavailableSection(); + } else if (!getSourceWithCharacters().hasCharacters()) { + sourceSection = getSourceWithCharacters().createUnavailableSection(); return sourceSection; } else { - sourceSection = co.getSourceSection(getSource()); + sourceSection = co.getSourceSection(getSourceWithCharacters()); return sourceSection; } } @@ -6218,7 +6231,7 @@ protected byte[] extractCode(Node node) { * * TODO We should revisit this when the AST interpreter is removed. */ - return MarshalModuleBuiltins.serializeCodeUnit(null, PythonContext.get(this), co); + return MarshalModuleBuiltins.serializeCodeUnit(null, getLanguage(), co); } @Override @@ -6229,7 +6242,7 @@ protected boolean isCloneUninitializedSupported() { @Override protected RootNode cloneUninitialized() { // Note: the bytecode might be quickened already, it's not practical to undo it - PBytecodeRootNode rootNode = new PBytecodeRootNode(getLanguage(), getFrameDescriptor(), getSignature(), co, lazySource, internal, parserCallbacks); + PBytecodeRootNode rootNode = new PBytecodeRootNode(getLanguage(), getFrameDescriptor(), getSignature(), co, source, internal, parserCallbacks); rootNode.variableTypes = variableTypes; return rootNode; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/RaiseNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/RaiseNode.java index 445211e1e0..5ffc63ccb0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/RaiseNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/RaiseNode.java @@ -50,6 +50,7 @@ import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Fallback; 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; @@ -63,11 +64,13 @@ import com.oracle.truffle.api.profiles.InlinedConditionProfile; @GenerateInline(false) // used in BCI root node +@GenerateUncached public abstract class RaiseNode extends PNodeWithContext { public abstract void execute(VirtualFrame frame, Object typeOrExceptionObject, Object cause, boolean rootNodeVisible); @ImportStatic(PGuards.class) @GenerateInline(false) // Not used in all specializations, better be lazy + @GenerateUncached public abstract static class SetExceptionCauseNode extends Node { public abstract void execute(VirtualFrame frame, Object exception, Object cause); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SendNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SendNode.java index 66d86e18d0..8fd4a716fa 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SendNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SendNode.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 @@ -44,13 +44,13 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.exception.PBaseException; -import com.oracle.graal.python.builtins.objects.exception.StopIterationBuiltins; import com.oracle.graal.python.builtins.objects.generator.CommonGeneratorBuiltins; import com.oracle.graal.python.builtins.objects.generator.PGenerator; 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.TpSlotIterNext.CallSlotTpIterNextNode; import com.oracle.graal.python.lib.IteratorExhausted; +import com.oracle.graal.python.lib.PyGenFetchStopIterationValue; import com.oracle.graal.python.lib.PyIterCheckNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.nodes.PNodeWithContext; @@ -77,7 +77,7 @@ static boolean doGenerator(VirtualFrame virtualFrame, int stackTop, PGenerator g @Bind Node inliningTarget, @Cached CommonGeneratorBuiltins.SendNode sendNode, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { try { Object value = sendNode.execute(virtualFrame, generator, arg); virtualFrame.setObject(stackTop, value); @@ -96,7 +96,7 @@ static boolean doIterator(VirtualFrame virtualFrame, int stackTop, Object iter, @Cached CallSlotTpIterNextNode callIterNext, @Exclusive @Cached InlinedBranchProfile exhaustedNoException, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { try { Object value = callIterNext.execute(virtualFrame, inliningTarget, slots.tp_iternext(), iter); virtualFrame.setObject(stackTop, value); @@ -121,7 +121,7 @@ static boolean doOther(VirtualFrame virtualFrame, int stackTop, Object obj, Obje @Bind Node inliningTarget, @Cached PyObjectCallMethodObjArgs callMethodNode, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { try { Object value = callMethodNode.execute(virtualFrame, inliningTarget, obj, T_SEND, arg); virtualFrame.setObject(stackTop, value); @@ -132,10 +132,9 @@ static boolean doOther(VirtualFrame virtualFrame, int stackTop, Object obj, Obje } } - private static void handleException(VirtualFrame frame, PException e, Node inliningTarget, IsBuiltinObjectProfile stopIterationProfile, StopIterationBuiltins.StopIterationValueNode getValue, - int stackTop) { + private static void handleException(VirtualFrame frame, PException e, Node inliningTarget, IsBuiltinObjectProfile stopIterationProfile, PyGenFetchStopIterationValue getValue, int stackTop) { e.expectStopIteration(inliningTarget, stopIterationProfile); - Object value = getValue.execute((PBaseException) e.getUnreifiedException()); + Object value = getValue.execute(inliningTarget, (PBaseException) e.getUnreifiedException()); frame.setObject(stackTop, null); frame.setObject(stackTop - 1, value); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SequenceFromStackNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SequenceFromStackNode.java index 5da2c6e35f..bc65d13570 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SequenceFromStackNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SequenceFromStackNode.java @@ -238,11 +238,6 @@ protected int getCapacityEstimate() { return initialCapacity.estimate(); } - @Override - public SourceSection getSourceSection() { - return null; - } - @Override public void reportUpdatedCapacity(ArrayBasedSequenceStorage newStore) { if (CompilerDirectives.inInterpreter()) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java index c807a2e99c..e745844fb5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java @@ -78,7 +78,7 @@ @GenerateUncached @ImportStatic(PArguments.class) @GenerateInline(false) // used in BCI root node -@OperationProxy.Proxyable(storeBytecodeIndex = true) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = true) public abstract class SetupAnnotationsNode extends PNodeWithContext { public abstract void execute(Frame frame); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ThrowNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ThrowNode.java index 69ae0e9209..46649e41c0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ThrowNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/ThrowNode.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,9 +47,9 @@ import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; import com.oracle.graal.python.builtins.objects.exception.GetEscapedExceptionNode; import com.oracle.graal.python.builtins.objects.exception.PBaseException; -import com.oracle.graal.python.builtins.objects.exception.StopIterationBuiltins; import com.oracle.graal.python.builtins.objects.generator.CommonGeneratorBuiltins; import com.oracle.graal.python.builtins.objects.generator.PGenerator; +import com.oracle.graal.python.lib.PyGenFetchStopIterationValue; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.WriteUnraisableNode; @@ -85,7 +85,7 @@ static boolean doGenerator(VirtualFrame frame, int stackTop, PGenerator generato @Exclusive @Cached GetEscapedExceptionNode getEscapedExceptionNode, @Exclusive @Cached IsBuiltinObjectProfile profileExit, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { Object exceptionObject = getEscapedExceptionNode.execute(inliningTarget, exception); if (profileExit.profileObject(inliningTarget, exceptionObject, GeneratorExit)) { closeNode.execute(frame, generator); @@ -115,7 +115,7 @@ static boolean doOther(VirtualFrame frame, int stackTop, Object obj, AbstractTru @Exclusive @Cached GetEscapedExceptionNode getEscapedExceptionNode, @Exclusive @Cached IsBuiltinObjectProfile profileExit, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { Object exceptionObject = getEscapedExceptionNode.execute(inliningTarget, exception); if (profileExit.profileObject(inliningTarget, exceptionObject, GeneratorExit)) { Object close = PNone.NO_VALUE; @@ -147,10 +147,10 @@ static boolean doOther(VirtualFrame frame, int stackTop, Object obj, AbstractTru } private static void handleException(VirtualFrame frame, Node inliningTarget, PException e, - IsBuiltinObjectProfile stopIterationProfile, StopIterationBuiltins.StopIterationValueNode getValue, + IsBuiltinObjectProfile stopIterationProfile, PyGenFetchStopIterationValue getValue, int stackTop) { e.expectStopIteration(inliningTarget, stopIterationProfile); - Object value = getValue.execute((PBaseException) e.getUnreifiedException()); + Object value = getValue.execute(inliningTarget, (PBaseException) e.getUnreifiedException()); frame.setObject(stackTop, null); frame.setObject(stackTop - 1, value); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/instrumentation/InstrumentationRootImpl.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/instrumentation/InstrumentationRootImpl.java index 4d4be9b7b3..9f4c734598 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/instrumentation/InstrumentationRootImpl.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/instrumentation/InstrumentationRootImpl.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 @@ -66,7 +66,7 @@ public InstrumentableNode materializeInstrumentableNodes(Set - * We cannot just use {@code @Cached} argument to cache the {@link PBytecodeDSLRootNode} created - * from the {@link BytecodeDSLCodeUnit}, because Truffle DSL permits races, and it could be - * initialized multiple times, which we must avoid to 1) not fill inline caches unnecessarily, 2) - * make use of the fact that comprehensions cannot change, so that we can invoke them using - * {@code DirectCallNode} without inline cache. - */ -public final class BytecodeDSLCodeUnitAndRoot { - private final BytecodeDSLCodeUnit codeUnit; - // updated via VarHandle - private PBytecodeDSLRootNode rootNode; - - public BytecodeDSLCodeUnitAndRoot(BytecodeDSLCodeUnit codeUnit) { - this.codeUnit = codeUnit; - } - - public BytecodeDSLCodeUnit getCodeUnit() { - return codeUnit; - } - - public PBytecodeDSLRootNode getRootNode(PBytecodeDSLRootNode outerRootNode) { - PBytecodeDSLRootNode existing = rootNode; - if (existing != null) { - return existing; - } - CompilerDirectives.transferToInterpreterAndInvalidate(); - PBytecodeDSLRootNode created = codeUnit.createRootNode(PythonContext.get(outerRootNode), outerRootNode.getSource()); - PBytecodeDSLRootNode prev = (PBytecodeDSLRootNode) ROOT_HANDLE.compareAndExchangeRelease(this, null, created); - return prev != null ? prev : created; - } - - private static final VarHandle ROOT_HANDLE; - static { - try { - ROOT_HANDLE = MethodHandles.lookup().findVarHandle(BytecodeDSLCodeUnitAndRoot.class, "rootNode", PBytecodeDSLRootNode.class); - } catch (ReflectiveOperationException e) { - throw new ExceptionInInitializerError(e); - } - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/MakeSetStorageNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/MakeSetStorageNode.java index a773a0b633..471c7bb672 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/MakeSetStorageNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/MakeSetStorageNode.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 @@ -42,7 +42,6 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; -import com.oracle.graal.python.builtins.objects.common.ObjectHashMap; import com.oracle.graal.python.builtins.objects.common.ObjectHashMap.PutNode; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.truffle.api.dsl.Cached; @@ -66,12 +65,12 @@ public static EconomicMapStorage doNonEmpty(VirtualFrame frame, Node inliningTar @Cached PyObjectHashNode hashNode, @Cached PutNode putNode) { int profiledLen = lengthProfile.profile(inliningTarget, elements.length); - ObjectHashMap map = new ObjectHashMap(profiledLen); + EconomicMapStorage storage = EconomicMapStorage.create(profiledLen); for (int i = 0; i < profiledLen; i++) { Object key = elements[i]; long keyHash = hashNode.execute(frame, inliningTarget, key); - putNode.put(frame, inliningTarget, map, key, keyHash, PNone.NONE); + putNode.put(frame, inliningTarget, storage, key, keyHash, PNone.NONE); } - return new EconomicMapStorage(map, false); + return storage; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java index 3474f52f69..f9fc7cb2de 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java @@ -59,10 +59,11 @@ import java.math.BigInteger; import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.modules.BuiltinFunctions.FormatNode; import com.oracle.graal.python.builtins.modules.MarshalModuleBuiltins; import com.oracle.graal.python.builtins.modules.TypingModuleBuiltins.CallTypingFuncObjectNode; import com.oracle.graal.python.builtins.modules.TypingModuleBuiltins.UnpackTypeVarTuplesNode; @@ -88,9 +89,9 @@ import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; import com.oracle.graal.python.builtins.objects.exception.PBaseException; import com.oracle.graal.python.builtins.objects.exception.PBaseExceptionGroup; -import com.oracle.graal.python.builtins.objects.exception.StopIterationBuiltins; import com.oracle.graal.python.builtins.objects.frame.PFrame; 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.PFunction; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.function.Signature; @@ -103,18 +104,27 @@ import com.oracle.graal.python.builtins.objects.iterator.PLongSequenceIterator; import com.oracle.graal.python.builtins.objects.iterator.PObjectSequenceIterator; 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.PMethod; +import com.oracle.graal.python.builtins.objects.module.ModuleBuiltins; +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.PythonObject; import com.oracle.graal.python.builtins.objects.set.PFrozenSet; import com.oracle.graal.python.builtins.objects.set.PSet; import com.oracle.graal.python.builtins.objects.set.SetNodes; import com.oracle.graal.python.builtins.objects.tuple.PTuple; +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.GetObjectSlotsNode; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.CallSlotTpIterNextNode; import com.oracle.graal.python.builtins.objects.typing.PTypeAliasType; import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.compiler.OpCodes.MakeTypeParamKind; import com.oracle.graal.python.compiler.ParserCallbacksImpl; import com.oracle.graal.python.lib.IteratorExhausted; +import com.oracle.graal.python.lib.PyGenFetchStopIterationValue; import com.oracle.graal.python.lib.PyIterCheckNode; import com.oracle.graal.python.lib.PyIterNextNode; import com.oracle.graal.python.lib.PyNumberAddNode; @@ -146,9 +156,10 @@ import com.oracle.graal.python.lib.PyNumberSubtractNode; import com.oracle.graal.python.lib.PyNumberTrueDivideNode; import com.oracle.graal.python.lib.PyNumberXorNode; -import com.oracle.graal.python.lib.PyObjectAsciiNode; +import com.oracle.graal.python.lib.PyObjectAsciiAsObjectNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectDelItem; +import com.oracle.graal.python.lib.PyObjectFormat; import com.oracle.graal.python.lib.PyObjectFunctionStr; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectGetItem; @@ -159,14 +170,15 @@ import com.oracle.graal.python.lib.PyObjectIsNotTrueNode; import com.oracle.graal.python.lib.PyObjectIsTrueNode; import com.oracle.graal.python.lib.PyObjectLookupAttr; -import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode; +import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; import com.oracle.graal.python.lib.PyObjectRichCompare.GenericRichCompare; import com.oracle.graal.python.lib.PyObjectSetAttr; import com.oracle.graal.python.lib.PyObjectSetAttrO; import com.oracle.graal.python.lib.PyObjectSetItem; import com.oracle.graal.python.lib.PyObjectSizeNode; -import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; +import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; import com.oracle.graal.python.lib.PySequenceContainsNode; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.BuiltinNames; import com.oracle.graal.python.nodes.ErrorMessages; @@ -180,8 +192,10 @@ import com.oracle.graal.python.nodes.argument.keywords.NonMappingException; import com.oracle.graal.python.nodes.argument.keywords.SameDictKeyException; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes; +import com.oracle.graal.python.nodes.builtins.TupleNodes.ConstructTupleNode; import com.oracle.graal.python.nodes.bytecode.CopyDictWithoutKeysNode; import com.oracle.graal.python.nodes.bytecode.GetAIterNode; import com.oracle.graal.python.nodes.bytecode.GetANextNode; @@ -196,7 +210,9 @@ import com.oracle.graal.python.nodes.bytecode.PrintExprNode; import com.oracle.graal.python.nodes.bytecode.RaiseNode; import com.oracle.graal.python.nodes.bytecode.SetupAnnotationsNode; +import com.oracle.graal.python.nodes.call.BoundDescriptor; import com.oracle.graal.python.nodes.call.CallDispatchers; +import com.oracle.graal.python.nodes.call.CallDispatchers.FunctionIndirectInvokeNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; import com.oracle.graal.python.nodes.call.special.CallQuaternaryMethodNode; @@ -237,12 +253,12 @@ import com.oracle.graal.python.runtime.sequence.PTupleListBase; import com.oracle.graal.python.runtime.sequence.storage.BoolSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.DoubleSequenceStorage; -import com.oracle.graal.python.runtime.sequence.storage.EmptySequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.IntSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.LongSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.graal.python.util.ArrayBuilder; +import com.oracle.graal.python.util.InlineWeakValueProfile; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; @@ -296,6 +312,8 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.object.DynamicObject; +import com.oracle.truffle.api.object.PropertyGetter; +import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.source.Source; @@ -306,6 +324,7 @@ @GenerateBytecode(// languageClass = PythonLanguage.class, // + sourceContentSupplier = "loadSourceContent", // illegalLocalException = PException.class, // illegalLocalExceptionFactory = "raiseUnboundLocalException", // enableBlockScoping = false, // @@ -314,8 +333,10 @@ enableTagInstrumentation = true, // boxingEliminationTypes = {int.class}, // tagTreeNodeLibrary = PTagTreeNodeExports.class, // - storeBytecodeIndexInFrame = true // -) + storeBytecodeIndexInFrame = true, // + defaultUncachedThreshold = "4", // + enableUncachedInterpreter = true, // + enableTailCallHandlers = true) @OperationProxy(PyNumberSubtractNode.class) @OperationProxy(PyNumberTrueDivideNode.class) @OperationProxy(PyNumberFloorDivideNode.class) @@ -344,10 +365,10 @@ @OperationProxy(PyNumberInPlaceLshiftNode.class) @OperationProxy(PyNumberInPlaceRshiftNode.class) @OperationProxy(IsNode.class) -@OperationProxy(FormatNode.class) +@OperationProxy(value = PyObjectFormat.class, name = "Format") @OperationProxy(ExceptMatchNode.class) -@OperationProxy(HandleExceptionsInHandlerNode.class) -@OperationProxy(EncapsulateExceptionGroupNode.class) +@OperationProxy(value = HandleExceptionsInHandlerNode.class, forceCached = true) +@OperationProxy(value = EncapsulateExceptionGroupNode.class, forceCached = true) @OperationProxy(GetYieldFromIterNode.class) @OperationProxy(GetAwaitableNode.class) @OperationProxy(SetupAnnotationsNode.class) @@ -363,13 +384,15 @@ @ShortCircuitOperation(name = "BoolOr", booleanConverter = PyObjectIsTrueNode.class, operator = Operator.OR_RETURN_VALUE) @ShortCircuitOperation(name = "PrimitiveBoolAnd", operator = Operator.AND_RETURN_VALUE) public abstract class PBytecodeDSLRootNode extends PRootNode implements BytecodeRootNode { - public static final int EXPLODE_LOOP_THRESHOLD = 30; private static final BytecodeConfig TRACE_AND_PROFILE_CONFIG = PBytecodeDSLRootNodeGen.newConfigBuilder().// addInstrumentation(TraceOrProfileCall.class).// addInstrumentation(TraceLine.class).// addInstrumentation(TraceLineAtLoopHeader.class).// + addInstrumentation(ClearTraceLine.class).// + addInstrumentation(InstrumentCallable.class).// + addInstrumentation(InstrumentCall.class).// + addInstrumentation(InstrumentCallReturn.class).// addInstrumentation(TraceOrProfileReturn.class).// - addInstrumentation(TraceException.class).// addInstrumentation(TraceLineWithArgument.class).// addInstrumentation(EnterInstrumentedRoot.class).// addInstrumentation(ResumeYieldGenerator.class).// @@ -397,8 +420,15 @@ private static final class TracingNodes extends Node { @CompilationFinal protected transient int classcellIndex; @CompilationFinal protected transient int instrumentationDataIndex; @CompilationFinal protected transient int yieldFromGeneratorIndex = -1; + @CompilationFinal protected transient int maxProfileCEventStackSize; @CompilationFinal(dimensions = 1) protected transient Assumption[] cellEffectivelyFinalAssumptions; + /* + * We don't want to store the assumption in MakeFunction node to be able to have an uncached version of it. + * So we put it into the root of the function that MakeFunction is creating. + */ + private final transient AtomicReference functionCodeFinalAssumption = new AtomicReference<>(); + private transient boolean pythonInternal; @CompilationFinal private transient boolean internal; @@ -411,26 +441,44 @@ protected PBytecodeDSLRootNode(PythonLanguage language, FrameDescriptor.Builder ((BytecodeDSLFrameInfo) getFrameDescriptor().getInfo()).setRootNode(this); } + public static Source loadSourceContent(PythonLanguage language, Source sourceWithoutContent) { + return language.getOrCreateSourceWithContent(sourceWithoutContent); + } + public static PBytecodeDSLRootNode cast(RootNode root) { return PBytecodeDSLRootNodeGen.BYTECODE.cast(root); } @TruffleBoundary public static void updateAllToTracingConfig(PythonLanguage language) { + PBytecodeDSLRootNodeGen.BYTECODE.update(language, BytecodeConfig.WITH_SOURCE); PBytecodeDSLRootNodeGen.BYTECODE.update(language, TRACE_AND_PROFILE_CONFIG); } + @Override + @TruffleBoundary + protected void prepareForInstrumentation(Set> materializedTags) { + super.prepareForInstrumentation(materializedTags); + PythonLanguage language = getLanguage(); + for (Object constant : co.constants) { + if (constant instanceof BytecodeDSLCodeUnit codeUnit) { + PBytecodeDSLRootNode rootNode = language.createCachedRootNode(l -> codeUnit.createRootNode(l, isInternal()), codeUnit); + rootNode.getCallTarget(); + } + } + } + public final PythonLanguage getLanguage() { return getLanguage(PythonLanguage.class); } - public void setMetadata(BytecodeDSLCodeUnit co, ParserCallbacksImpl parserErrorCallback) { + public void setMetadata(BytecodeDSLCodeUnit co, ParserCallbacksImpl parserErrorCallback, boolean internal) { CompilerDirectives.transferToInterpreterAndInvalidate(); this.co = co; this.signature = co.computeSignature(); this.classcellIndex = co.classcellIndex; this.selfIndex = co.selfIndex; - this.internal = getSource().isInternal(); + this.internal = internal; this.parserErrorCallback = parserErrorCallback; if (co.cellvars.length > 0) { this.cellEffectivelyFinalAssumptions = new Assumption[co.cellvars.length]; @@ -440,6 +488,8 @@ public void setMetadata(BytecodeDSLCodeUnit co, ParserCallbacksImpl parserErrorC } instrumentationDataIndex = co.instrumentationDataIndex; yieldFromGeneratorIndex = co.yieldFromGeneratorIndex; + maxProfileCEventStackSize = co.maxProfileCEventStackSize; + PythonOptions.setUncachedInterpreterThreshold(getLanguage(), getBytecodeNode()); } @Override @@ -472,6 +522,7 @@ public static final class EnterCalleeContext { @Specialization public static void doEnter(VirtualFrame frame, @Bind PBytecodeDSLRootNode root) { + assert PArguments.getFunctionOrCodeObject(frame) != null; root.calleeContext.enter(frame, root); } } @@ -482,19 +533,28 @@ private InstrumentationData getInstrumentationData(VirtualFrame frame, BytecodeN // This should only happen when this root was on stack when the config was updated. It // should have been deoptimized anyway when the stack was unwound back to it. CompilerDirectives.transferToInterpreterAndInvalidate(); - current = new InstrumentationData(); + current = new InstrumentationData(maxProfileCEventStackSize); bytecode.setLocalValue(0, frame, instrumentationDataIndex, current); } return current; } - private void resetInstrumenationData(VirtualFrame frame, BytecodeNode bytecode) { + private void resetInstrumentationData(VirtualFrame frame, BytecodeNode bytecode) { InstrumentationData current = (InstrumentationData) bytecode.getLocalValue(0, frame, instrumentationDataIndex); if (current == null) { - current = new InstrumentationData(); - bytecode.setLocalValue(0, frame, instrumentationDataIndex, current); + initInstrumentationData(frame, bytecode); + } else { + current.reset(); } - current.reset(); + } + + private void initInstrumentationData(VirtualFrame frame, BytecodeNode bytecode) { + bytecode.setLocalValue(0, frame, instrumentationDataIndex, new InstrumentationData(maxProfileCEventStackSize)); + } + + private void resetInstrumentationDataForResume(VirtualFrame frame, BytecodeNode bytecode, int bci) { + resetInstrumentationData(frame, bytecode); + getInstrumentationData(frame, bytecode).setNonClearingPastLine(bciToLine(bci, bytecode)); } @Instrumentation(storeBytecodeIndex = false) @@ -503,7 +563,7 @@ public static final class EnterInstrumentedRoot { public static void doEnter(VirtualFrame frame, @Bind PBytecodeDSLRootNode root, @Bind BytecodeNode bytecode) { - bytecode.setLocalValue(0, frame, root.instrumentationDataIndex, new InstrumentationData()); + root.initInstrumentationData(frame, bytecode); } } @@ -513,6 +573,9 @@ public static final class EpilogForReturn { public static Object doExit(VirtualFrame frame, Object returnValue, @Bind PBytecodeDSLRootNode root, @Bind BytecodeNode location) { + if (root.needsTraceAndProfileInstrumentation()) { + root.traceOrProfileReturn(frame, location, returnValue); + } root.calleeContext.exit(frame, root, location); return returnValue; } @@ -535,16 +598,45 @@ public static void doExit(VirtualFrame frame, AbstractTruffleException ate, } } - /* - * Data for tracing, profiling and instrumentation + /** + * Data for tracing, profiling and instrumentation. This data is stored in a dedicated frame + * slot when instrumentation is enabled. + * + *

      + * Builtin C profile events are implemented by wrapping calls with Bytecode DSL + * {@code @Instrumentation} operations using this structure: + * + *

      {@code
      +     * InstrumentCallReturn    // report C_RETURN and pop profileCEventCallables
      +     *     Call
      +     *         InstrumentCallable    // push the callable before the call consumes it
      +     *             callable-expression
      +     *         arg1-expression
      +     *         ...
      +     *         InstrumentCall        // report C_CALL and set profileCEventCallStarted
      +     *             argN-expression
      +     * }
      +     * 
      + * + * In {@code interceptTruffleException} we check whether a {@code C_CALL} is underway. If so, + * we report the {@code C_EXCEPTION} event. + * + *

      + * A simpler implementation should be possible if GR-71168 adds support for replacing call + * operations directly, e.g. {@code @Instrumentation(replaces = Call.class)}. */ public static final class InstrumentationData { private int pastLine; // Sometimes, we need to use pastLine value after it has been cleared. Implicit returns in // combination with loops are one such scenario. private int nonClearingPastLine; + // null entries are non-builtin calls that must still preserve nested C-call stack shape + private final Object[] profileCEventCallables; + private int profileCEventStackTop; + private boolean profileCEventCallStarted; - public InstrumentationData() { + public InstrumentationData(int maxProfileCEventStackSize) { + this.profileCEventCallables = new Object[maxProfileCEventStackSize]; reset(); } @@ -571,6 +663,70 @@ void setNonClearingPastLine(int value) { public void reset() { this.pastLine = -1; this.nonClearingPastLine = -1; + clearProfileCEventCallableStack(); + } + + boolean hasProfileCEventCallables() { + return profileCEventStackTop > 0; + } + + Object exitProfileCall() { + if (profileCEventStackTop == 0) { + return null; + } + boolean callStarted = profileCEventCallStarted; + Object result = popProfileCEventCallable(); + return callStarted ? result : null; + } + + void pushProfileCEventCallable(Object callable) { + assert profileCEventStackTop < profileCEventCallables.length; + profileCEventCallables[profileCEventStackTop] = callable; + profileCEventStackTop++; + profileCEventCallStarted = false; + } + + private Object popProfileCEventCallable() { + profileCEventStackTop--; + Object result = profileCEventCallables[profileCEventStackTop]; + profileCEventCallables[profileCEventStackTop] = null; + profileCEventCallStarted = false; + return result; + } + + void startProfileCEventCall(PBytecodeDSLRootNode root, VirtualFrame frame, BytecodeNode location) { + if (profileCEventStackTop > 0) { + Object profileArg = profileCEventCallables[profileCEventStackTop - 1]; + profileCEventCallStarted = profileArg != null && root.profileCEvent(frame, location, profileArg, ProfileEvent.C_CALL); + } + } + + void clearProfileCEventCallableStack() { + if (profileCEventCallables != null) { + PythonUtils.fill(profileCEventCallables, 0, profileCEventStackTop, null); + } + profileCEventStackTop = 0; + profileCEventCallStarted = false; + } + + // called when we intercept an exception; report C_EXCEPTION only for a C call that actually started + void profileCEventCallablesForException(PBytecodeDSLRootNode root, VirtualFrame frame, BytecodeNode location) { + int oldStackTop = profileCEventStackTop; + boolean callStarted = profileCEventCallStarted; + profileCEventStackTop = 0; + try { + if (callStarted && oldStackTop > 0) { + Object profileArg = profileCEventCallables[oldStackTop - 1]; + if (profileArg != null) { + root.profileCEvent(frame, location, profileArg, ProfileEvent.C_EXCEPTION); + } + } + } finally { + for (int i = oldStackTop - 1; i >= 0; i--) { + profileCEventCallables[i] = null; + } + profileCEventCallStarted = false; + } } } @@ -629,6 +785,45 @@ private void invokeProfileFunction(VirtualFrame virtualFrame, BytecodeNode locat } } + private static Object getBuiltinProfileArg(Object callable) { + Object unwrappedCallable = unwrapBoundDescriptor(callable); + if (isBuiltin(unwrappedCallable)) { + return unwrappedCallable; + } + if (unwrappedCallable instanceof PMethod method && isBuiltin(method.getFunction())) { + return method.getFunction(); + } + return null; + } + + private static Object unwrapBoundDescriptor(Object callable) { + return callable instanceof BoundDescriptor boundDescriptor ? boundDescriptor.descriptor : callable; + } + + private static boolean isBuiltin(Object callable) { + return callable instanceof PBuiltinFunction || callable instanceof PBuiltinMethod; + } + + @InliningCutoff + private boolean profileCEvent(VirtualFrame virtualFrame, BytecodeNode location, Object arg, PythonContext.ProfileEvent event) { + PythonThreadState threadState = getThreadState(); + Object profileFun = threadState.getProfileFun(); + if (profileFun != null && !threadState.isProfiling()) { + invokeProfileFunction(virtualFrame, location, profileFun, threadState, event, arg); + return true; + } + return false; + } + + @InliningCutoff + private void profilePendingCExceptions(VirtualFrame frame, BytecodeNode location) { + getInstrumentationData(frame, location).profileCEventCallablesForException(this, frame, location); + } + + private void clearPendingCExceptions(VirtualFrame frame, BytecodeNode location) { + getInstrumentationData(frame, location).clearProfileCEventCallableStack(); + } + @InliningCutoff private void invokeTraceFunction(VirtualFrame virtualFrame, BytecodeNode location, Object traceFun, PythonContext.PythonThreadState threadState, PythonContext.TraceEvent event, Object arg, int line) { @@ -683,6 +878,7 @@ private void invokeTraceFunction(VirtualFrame virtualFrame, BytecodeNode locatio } } + @InliningCutoff private void traceOrProfileCall(VirtualFrame frame, BytecodeNode bytecode, int bci) { PythonThreadState threadState = getThreadState(); Object traceFun = threadState.getTraceFun(); @@ -750,6 +946,13 @@ private void traceLineAtLoopHeader(VirtualFrame frame, BytecodeNode location, in } } + @InliningCutoff + private void clearTraceLine(VirtualFrame frame, BytecodeNode location) { + InstrumentationData instrumentationData = getInstrumentationData(frame, location); + instrumentationData.clearPastLine(); + } + + @InliningCutoff private void traceOrProfileReturn(VirtualFrame frame, BytecodeNode location, Object value) { PythonThreadState threadState = getThreadState(); Object traceFun = threadState.getTraceFun(); @@ -769,6 +972,7 @@ private PException traceException(VirtualFrame frame, BytecodeNode bytecode, int PException result = pe; PythonThreadState threadState = getThreadState(); + profilePendingCExceptions(frame, bytecode); // We should only trace the exception if tracing is enabled. if (threadState.getTraceFun() != null && !pe.getShouldTrace()) { PFrame pyFrame = ensurePyFrame(frame, bytecode); @@ -833,6 +1037,80 @@ public static void perform(VirtualFrame frame, } } + @Instrumentation(storeBytecodeIndex = false) + public static final class ClearTraceLine { + @Specialization + public static void perform(VirtualFrame frame, + @Bind BytecodeNode location, + @Bind PBytecodeDSLRootNode root) { + root.clearTraceLine(frame, location); + } + } + + @Instrumentation(storeBytecodeIndex = true) + public static final class InstrumentCallable { + @Specialization + public static Object perform(VirtualFrame frame, Object callable, + @Bind BytecodeNode location, + @Bind PBytecodeDSLRootNode root) { + PythonThreadState threadState = root.getThreadState(); + if (threadState.isProfiling()) { + return callable; + } + InstrumentationData instrumentationData = root.getInstrumentationData(frame, location); + if (threadState.getProfileFun() == null) { + if (instrumentationData.hasProfileCEventCallables()) { + instrumentationData.pushProfileCEventCallable(null); + } + return callable; + } + Object profileArg = getBuiltinProfileArg(callable); + if (profileArg != null) { + instrumentationData.pushProfileCEventCallable(profileArg); + } else if (instrumentationData.hasProfileCEventCallables()) { + instrumentationData.pushProfileCEventCallable(null); + } + return callable; + } + } + + @Instrumentation(storeBytecodeIndex = true) + public static final class InstrumentCall { + @Specialization + public static Object perform(VirtualFrame frame, Object value, + @Bind BytecodeNode location, + @Bind PBytecodeDSLRootNode root) { + PythonThreadState threadState = root.getThreadState(); + if (!threadState.isProfiling()) { + root.getInstrumentationData(frame, location).startProfileCEventCall(root, frame, location); + } + return value; + } + } + + @Instrumentation(storeBytecodeIndex = true) + public static final class InstrumentCallReturn { + @Specialization + public static Object perform(VirtualFrame frame, Object value, + @Bind BytecodeNode location, + @Bind PBytecodeDSLRootNode root) { + PythonThreadState threadState = root.getThreadState(); + if (threadState.isProfiling()) { + return value; + } + InstrumentationData instrumentationData = root.getInstrumentationData(frame, location); + if (threadState.getProfileFun() == null) { + instrumentationData.exitProfileCall(); + return value; + } + Object profileArg = instrumentationData.exitProfileCall(); + if (profileArg != null) { + root.profileCEvent(frame, location, profileArg, ProfileEvent.C_RETURN); + } + return value; + } + } + @Instrumentation(storeBytecodeIndex = true) public static final class TraceOrProfileReturn { @Specialization @@ -858,16 +1136,11 @@ public static Object perform(VirtualFrame frame, Object value, int line, } } - @Instrumentation - public static final class TraceException { - @Specialization - public static void perform() { - throw new UnsupportedOperationException("trace exception not implemented"); - } - } - @Override public Throwable interceptInternalException(Throwable throwable, VirtualFrame frame, BytecodeNode bytecodeNode, int bci) { + if (needsTraceAndProfileInstrumentation()) { + clearPendingCExceptions(frame, bytecodeNode); + } PythonLanguage language = getLanguage(); if (language.getEngineOption(PythonOptions.CatchAllExceptions) && (throwable instanceof Exception || throwable instanceof AssertionError)) { return ExceptionUtils.wrapJavaException(throwable, this, PFactory.createBaseException(language, SystemError, ErrorMessages.M, new Object[]{throwable})); @@ -891,7 +1164,7 @@ public AbstractTruffleException interceptTruffleException(AbstractTruffleExcepti getCaughtExceptionNode = insert(ExceptionStateNodes.GetCaughtExceptionNode.create()); } AbstractTruffleException context = getCaughtExceptionNode.execute(frame); - if (context instanceof PException pe2) { + if (context instanceof PException pe2 && !pe.isReraised()) { if (chainExceptionsNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); chainExceptionsNode = insert(ChainExceptionsNode.create()); @@ -940,14 +1213,6 @@ public int getFirstLineno() { return co.startLine; } - protected Source getSource() { - SourceSection section = getSourceSection(); - if (section == null) { - return PythonUtils.createFakeSource(); - } - return section.getSource(); - } - @Override public abstract boolean isCaptureFramesForTrace(boolean compiledFrame); @@ -981,9 +1246,11 @@ public SourceSection getSourceSectionForLocation(BytecodeLocation location) { @TruffleBoundary public SourceSection getSourceSectionForLocation(int bci, BytecodeNode bytecodeNode) { - BytecodeLocation location = null; - if (bytecodeNode != null) { - location = bytecodeNode.getBytecodeLocation(bci); + BytecodeLocation location; + try { + location = bytecodeNode.getBytecodeLocation(bci).ensureSourceInformation(); + } catch (MarshalModuleBuiltins.ReparseError e) { + throw PRaiseNode.raiseStatic(bytecodeNode, SystemError, ErrorMessages.FAILED_TO_REPARSE_BYTECODE_FILE); } return getSourceSectionForLocation(location); } @@ -1027,7 +1294,7 @@ public static int lastiToBci(int lasti, BytecodeNode bytecodeNode) { @Override protected byte[] extractCode(Node node) { - return MarshalModuleBuiltins.serializeCodeUnit(node, PythonContext.get(node), co); + return MarshalModuleBuiltins.serializeCodeUnit(node, getLanguage(), co); } private static Object checkUnboundCell(PCell cell, int index, BytecodeNode bytecodeNode) { @@ -1094,6 +1361,18 @@ public boolean hasYieldFromGenerator() { return yieldFromGeneratorIndex != -1; } + public Assumption getFunctionCodeFinalAssumption() { + CompilerAsserts.neverPartOfCompilation(); + Assumption assumption = functionCodeFinalAssumption.get(); + if (assumption == null) { + assumption = Truffle.getRuntime().createAssumption("code stable assumption"); + if (!functionCodeFinalAssumption.compareAndSet(null, assumption)) { + assumption = functionCodeFinalAssumption.get(); + } + } + return assumption; + } + @Operation @ConstantOperand(type = int.class) public static final class ArrayIndex { @@ -1132,7 +1411,6 @@ public static Object doOther(Object ex) { * provides access to the generator frame even before the generator was started. */ @Yield - @SuppressWarnings("truffle-interpreted-performance") // blocked by GR-69979 public static final class YieldGenerator { @Specialization public static Object doYield( @@ -1173,7 +1451,7 @@ public static Object doObject(VirtualFrame frame, Object generator, @Bind PBytecodeDSLRootNode root, @Bind BytecodeNode bytecode, @Bind("$bytecodeIndex") int bci) { - root.resetInstrumenationData(frame, bytecode); + root.resetInstrumentationDataForResume(frame, bytecode, bci); root.traceOrProfileCall(frame, bytecode, bci); return generator; } @@ -1195,7 +1473,6 @@ public static Object doObject(Object value, * Performs some clean-up steps before suspending execution, and updates the generator state. */ @Yield - @SuppressWarnings("truffle-interpreted-performance") // blocked by GR-69979 public static final class YieldValue { @Specialization public static Object doObject(Object value, @@ -1259,7 +1536,7 @@ static Object readFromLocalsFastPath(VirtualFrame frame, TruffleString attribute @ForceQuickening @Specialization(guards = "!isNoValue(result)", limit = "1") public static Object doLocalFastPath(VirtualFrame frame, TruffleString name, - @Cached ReadAttributeFromPythonObjectNode readAttrNode, + @Cached(inline = false) ReadAttributeFromPythonObjectNode readAttrNode, @Bind("readFromLocalsFastPath(frame, name, readAttrNode)") Object result) { return result; } @@ -1399,30 +1676,30 @@ public static Object perform(VirtualFrame frame, Object receiver, @Operation(storeBytecodeIndex = true) public static final class FormatStr { @Specialization - public static TruffleString perform(VirtualFrame frame, Object object, + public static Object perform(VirtualFrame frame, Object object, @Bind Node inliningTarget, - @Cached PyObjectStrAsTruffleStringNode asTruffleStringNode) { - return asTruffleStringNode.execute(frame, inliningTarget, object); + @Cached PyObjectStrAsObjectNode strNode) { + return strNode.execute(frame, inliningTarget, object); } } @Operation(storeBytecodeIndex = true) public static final class FormatRepr { @Specialization - public static TruffleString perform(VirtualFrame frame, Object object, + public static Object perform(VirtualFrame frame, Object object, @Bind Node inliningTarget, - @Cached PyObjectReprAsTruffleStringNode asTruffleStringNode) { - return asTruffleStringNode.execute(frame, inliningTarget, object); + @Cached PyObjectReprAsObjectNode reprNode) { + return reprNode.execute(frame, inliningTarget, object); } } @Operation(storeBytecodeIndex = true) public static final class FormatAscii { @Specialization - public static TruffleString perform(VirtualFrame frame, Object object, + public static Object perform(VirtualFrame frame, Object object, @Bind Node inliningTarget, - @Cached PyObjectAsciiNode asTruffleStringNode) { - return asTruffleStringNode.execute(frame, inliningTarget, object); + @Cached PyObjectAsciiAsObjectNode asciiNode) { + return asciiNode.execute(frame, inliningTarget, object); } } @@ -1462,38 +1739,42 @@ public static Object perform(VirtualFrame frame, LocalAccessor attributes, Objec @Operation(storeBytecodeIndex = true) @ConstantOperand(type = TruffleString.class, name = "name") @ConstantOperand(type = TruffleString.class, name = "qualifiedName") - @ConstantOperand(type = BytecodeDSLCodeUnitAndRoot.class) + @ConstantOperand(type = int.class) public static final class MakeFunction { - @Specialization(guards = "isSingleContext(rootNode)") + @Specialization(guards = "isSingleContext(rootNode)", excludeForUncached = true) public static Object functionSingleContext(VirtualFrame frame, TruffleString name, TruffleString qualifiedName, - BytecodeDSLCodeUnitAndRoot codeUnit, + @SuppressWarnings("unused") int codeIndex, Object[] defaults, Object[] kwDefaultsObject, Object closure, Object annotations, @Bind PBytecodeDSLRootNode rootNode, - @Cached("createCode(rootNode, codeUnit)") PCode cachedCode, + @Bind("getCodeUnit(rootNode, codeIndex)") BytecodeDSLCodeUnit codeUnit, + @Cached("getCode(frame, codeIndex, codeUnit)") PCode code, + @Cached(value = "getCodeStableAssumption(code)", uncached = "getCodeStableAssumption(code)") Assumption codeStableAssumption, @Shared @Cached DynamicObject.PutNode putNode) { - return createFunction(frame, name, qualifiedName, codeUnit.getCodeUnit().getDocstring(), - cachedCode, defaults, kwDefaultsObject, closure, annotations, rootNode, putNode); + return createFunction(frame, name, qualifiedName, codeUnit.getDocstring(), + code, defaults, kwDefaultsObject, closure, annotations, codeStableAssumption, rootNode, putNode); } @Specialization(replaces = "functionSingleContext") public static Object functionMultiContext(VirtualFrame frame, TruffleString name, TruffleString qualifiedName, - BytecodeDSLCodeUnitAndRoot codeUnit, + @SuppressWarnings("unused") int codeIndex, Object[] defaults, Object[] kwDefaultsObject, Object closure, Object annotations, @Bind PBytecodeDSLRootNode rootNode, + @Bind("getCodeUnit(rootNode, codeIndex)") BytecodeDSLCodeUnit codeUnit, + @Bind("getCode(frame, codeIndex, codeUnit)") PCode code, + @Cached(value = "getCodeStableAssumption(code)", uncached = "getCodeStableAssumption(code)") Assumption codeStableAssumption, @Shared @Cached DynamicObject.PutNode putNode) { - PCode code = createCode(rootNode, codeUnit); - return createFunction(frame, name, qualifiedName, codeUnit.getCodeUnit().getDocstring(), - code, defaults, kwDefaultsObject, closure, annotations, rootNode, putNode); + return createFunction(frame, name, qualifiedName, codeUnit.getDocstring(), + code, defaults, kwDefaultsObject, closure, annotations, codeStableAssumption, rootNode, putNode); } @Idempotent @@ -1502,26 +1783,33 @@ protected static boolean isSingleContext(Node node) { } @NeverDefault - protected static PCode createCode(PBytecodeDSLRootNode outerRootNode, BytecodeDSLCodeUnitAndRoot codeUnit) { - PBytecodeDSLRootNode rootNode = codeUnit.getRootNode(outerRootNode); - return PFactory.createCode( - PythonLanguage.get(outerRootNode), - rootNode.getCallTarget(), - rootNode.getSignature(), - codeUnit.getCodeUnit()); + static Assumption getCodeStableAssumption(PCode code) { + PBytecodeDSLRootNode rootNode = (PBytecodeDSLRootNode) code.getRootNode(); + return rootNode.getFunctionCodeFinalAssumption(); + } + + @NeverDefault + protected static PCode getCode(VirtualFrame frame, int codeIndex, BytecodeDSLCodeUnit codeUnit) { + PCode thisCode = PArguments.getCodeObject(frame); + return thisCode.getOrCreateChildCode(codeIndex, codeUnit); + } + + protected static BytecodeDSLCodeUnit getCodeUnit(PBytecodeDSLRootNode rootNode, int codeIndex) { + return (BytecodeDSLCodeUnit) rootNode.getCodeUnit().constants[codeIndex]; } protected static PFunction createFunction(VirtualFrame frame, TruffleString name, TruffleString qualifiedName, TruffleString doc, PCode code, Object[] defaults, Object[] kwDefaultsObject, Object closure, Object annotations, - PBytecodeDSLRootNode node, + Assumption codeStableAssumption, PBytecodeDSLRootNode node, DynamicObject.PutNode putNode) { PKeyword[] kwDefaults = new PKeyword[kwDefaultsObject.length]; // Note: kwDefaultsObject should be a result of operation MakeKeywords, which produces // PKeyword[] PythonUtils.arraycopy(kwDefaultsObject, 0, kwDefaults, 0, kwDefaults.length); - PFunction function = PFactory.createFunction(PythonLanguage.get(node), name, qualifiedName, code, PArguments.getGlobals(frame), defaults, kwDefaults, (PCell[]) closure); + PFunction function = PFactory.createFunction(PythonLanguage.get(node), name, qualifiedName, code, PArguments.getGlobals(frame), defaults, kwDefaults, (PCell[]) closure, + codeStableAssumption); if (annotations != null) { putNode.execute(function, T___ANNOTATIONS__, annotations); @@ -1646,26 +1934,188 @@ public static Object doIt(VirtualFrame frame, @Operation(storeBytecodeIndex = true) @ConstantOperand(type = TruffleString.class) + @ImportStatic({PGuards.class, TpSlots.class, PythonUtils.class}) public static final class GetAttribute { - @Specialization + // Builtin module object fast-path: we know there aren't any descriptors for other than + // dunder (__xxx__) names + public static Object loadModuleValue(PythonModule object, Shape cachedShape, PropertyGetter cachedPropertyGetter) { + // GetClass.GetPythonObjectClassNode would cache on the shape if it can, and read the + // dynamic type from there unless it observes objects where the type was changed. This + // is rare enough that we can pay the price of a useless read here. + Object type = cachedShape.getDynamicType(); + if (type != PythonBuiltinClassType.PythonModule) { + return null; + } + + assert object.checkDictFlags(); + if ((cachedShape.getFlags() & (PythonObject.HAS_MATERIALIZED_DICT)) == 0) { + Object value = cachedPropertyGetter.get(object); + return value == PNone.NO_VALUE ? null : value; + } + + return null; + } + + @ForceQuickening + @Specialization(guards = {"cachedPropertyGetter != null", "cachedPropertyGetter.accepts(receiver)", "value != null", + "!canBeSpecialMethod(key, codePointLengthNode, codePointAtIndexNode)"}, limit = "3") + static Object doModule(TruffleString key, PythonModule receiver, + @Cached("receiver.getShape()") Shape cachedShape, + @Cached("getPropertyGetterWithFinalAssumption(cachedShape, key)") PropertyGetter cachedPropertyGetter, + @Exclusive @Cached TruffleString.CodePointLengthNode codePointLengthNode, + @Exclusive @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode, + @Bind("loadModuleValue(receiver, cachedShape, cachedPropertyGetter)") Object value) { + return value; + } + + // For type instance field: for builtin type we know descriptors only have dunder names + // (__xxx__), so we can skip descriptor check + we need to check the __get__ (tp_descr_get) + // on the resulting value (this is common situation) + public static Object loadTypeInstanceValue(VirtualFrame frame, Node inliningTarget, PythonManagedClass object, GetObjectSlotsNode getValueSlotsNode, + CallSlotDescrGet callSlotDescrGet, Shape cachedShape, PropertyGetter cachedPropertyGetter, InlinedBranchProfile hasNonDescriptorValueProfile) { + Object type = cachedShape.getDynamicType(); + if (type != PythonBuiltinClassType.PythonClass) { + return null; + } + assert object.checkDictFlags(); + if ((cachedShape.getFlags() & (PythonObject.HAS_MATERIALIZED_DICT)) == 0) { + Object value = cachedPropertyGetter.get(object); + if (value != PNone.NO_VALUE && value != null) { + var valueGet = getValueSlotsNode.execute(inliningTarget, value).tp_descr_get(); + if (valueGet == null) { + hasNonDescriptorValueProfile.enter(inliningTarget); + return value; + } else { + return callSlotDescrGet.execute(frame, inliningTarget, valueGet, value, PNone.NO_VALUE, object); + } + } + } + return null; + } + + @ForceQuickening + @Specialization(guards = {"cachedPropertyGetter != null", "cachedPropertyGetter.accepts(receiver)", "value != null", + "!canBeSpecialMethod(key, codePointLengthNode, codePointAtIndexNode)"}, limit = "3") + static Object doType(VirtualFrame frame, TruffleString key, PythonManagedClass receiver, + @Cached("receiver.getShape()") Shape cachedShape, + @Cached("getPropertyGetterWithFinalAssumption(cachedShape, key)") PropertyGetter cachedPropertyGetter, + @Cached GetObjectSlotsNode getObjectSlotsNode, + @Cached CallSlotDescrGet callSlotDescrGet, + @Cached InlinedBranchProfile hasNonDescriptorValueProfile, + @Exclusive @Cached TruffleString.CodePointLengthNode codePointLengthNode, + @Exclusive @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode, + @Bind("loadTypeInstanceValue(frame, $node, receiver, getObjectSlotsNode, callSlotDescrGet, cachedShape, cachedPropertyGetter, hasNonDescriptorValueProfile)") Object value) { + return value; + } + + // Object instance field fast-path: for cases where there is no descriptor, and it's just + // simple DOM property read + public static Object loadInstanceValue(Node inliningTarget, PythonObject object, LookupAttributeInMRONode getDesc, Shape cachedShape, PropertyGetter cachedPropertyGetter, + InlineWeakValueProfile slotsValueProfile) { + TpSlots slots; + Object type = cachedShape.getDynamicType(); + // If this path works out, PropertyGetter.accepts() guards on the shape. + // After PE the final dynamicType field should dominate the branch and PE should remove + // the slots branch it doesn't need. The + // PythonBuiltinClassType slots are final, so PE can use that, but PythonManagedClass + // slots are not, so we should probably profile? + if (type instanceof PythonBuiltinClassType pbct) { + slots = pbct.getSlots(); + } else if (type instanceof PythonManagedClass klass) { + slots = slotsValueProfile.execute(inliningTarget, klass.getTpSlots()); + } else { + return null; + } + // The next check will fold after PE if the pbct was constant, which is implied by the + // guard in getDesc + if (slots.tp_getattro() == ObjectBuiltins.SLOTS.tp_getattro() || + slots.tp_getattro() == ModuleBuiltins.SLOTS.tp_getattro()) { + Object descr = getDesc.execute(type); + if (descr == PNone.NO_VALUE) { + assert object.checkDictFlags(); + if ((cachedShape.getFlags() & (PythonObject.HAS_MATERIALIZED_DICT)) == 0) { + Object value = cachedPropertyGetter.get(object); + // Note: the NO_VALUE check is harmless for PE, because it leads to a deopt + // anyway + return value == PNone.NO_VALUE ? null : value; + } + } + } + return null; + } + + @ForceQuickening + @Specialization(guards = {"cachedPropertyGetter != null", "cachedPropertyGetter.accepts(receiver)", "value != null"}, replaces = "doModule", limit = "3") + static Object doInstanceValue(TruffleString key, PythonObject receiver, + @Bind Node inliningTarget, + @Cached("receiver.getShape()") Shape cachedShape, + @Cached("getPropertyGetterWithFinalAssumption(cachedShape, key)") PropertyGetter cachedPropertyGetter, + @Cached("create(key)") LookupAttributeInMRONode getDesc, + @Cached InlineWeakValueProfile slotsValueProfile, + @Bind("loadInstanceValue(inliningTarget, receiver, getDesc, cachedShape, cachedPropertyGetter, slotsValueProfile)") Object value) { + return value; + } + + @Specialization(excludeForUncached = true, replaces = {"doInstanceValue", "doType"}) public static Object doIt(VirtualFrame frame, - TruffleString name, + TruffleString key, Object obj, - @Cached("create(name)") GetFixedAttributeNode getAttributeNode) { + @Cached("create(key)") GetFixedAttributeNode getAttributeNode) { return getAttributeNode.execute(frame, obj); } + + @Specialization(replaces = "doIt") + @InliningCutoff + public static Object doItUncached(VirtualFrame frame, TruffleString key, Object obj, + @Bind Node inliningTargetForDummy, + @Cached PyObjectGetAttr dummyToForceStoreBCI) { + return PyObjectGetAttr.getUncached().execute(frame, null, obj, key); + } } @Operation(storeBytecodeIndex = true) @ConstantOperand(type = TruffleString.class) public static final class SetAttribute { - @Specialization + public static boolean canStoreInstanceValue(Node inliningTarget, PythonObject object, Shape cachedShape, LookupAttributeInMRONode getDesc, + GetObjectSlotsNode getDescSlotsNode, InlineWeakValueProfile slotsValueProfile) { + TpSlots slots; + Object type = cachedShape.getDynamicType(); + if (type instanceof PythonBuiltinClassType pbct) { + slots = pbct.getSlots(); + } else if (type instanceof PythonManagedClass klass) { + slots = slotsValueProfile.execute(inliningTarget, klass.getTpSlots()); + } else { + return false; + } + if (slots.tp_setattro() == ObjectBuiltins.SLOTS.tp_setattro()) { + Object descr = getDesc.execute(type); + if (descr == PNone.NO_VALUE || getDescSlotsNode.execute(inliningTarget, descr).tp_descr_set() == null) { + assert object.checkDictFlags(); + return (cachedShape.getFlags() & (PythonObject.HAS_MATERIALIZED_DICT | PythonObject.HAS_SLOTS_BUT_NO_DICT_FLAG)) == 0; + } + } + return false; + } + + @ForceQuickening + @Specialization(guards = {"cachedShape.check(receiver)", "canStoreInstanceValue(inliningTarget, receiver, cachedShape, getDesc, getDescSlotsNode, slotsValueProfile)"}, limit = "3") + static void doInstanceValue(TruffleString key, Object value, PythonObject receiver, + @Bind Node inliningTarget, + @Cached("receiver.getShape()") Shape cachedShape, + @Cached("create(key)") LookupAttributeInMRONode getDesc, + @Cached GetObjectSlotsNode getDescSlotsNode, + @Cached InlineWeakValueProfile slotsValueProfile, + @Cached DynamicObject.PutNode putNode) { + putNode.execute(receiver, key, value); + } + + @Specialization(replaces = "doInstanceValue") public static void doIt(VirtualFrame frame, TruffleString key, Object value, Object object, @Bind Node inliningTarget, - @Cached PyObjectSetAttr setAttrNode) { + @Exclusive @Cached PyObjectSetAttr setAttrNode) { setAttrNode.execute(frame, inliningTarget, object, key, value); } } @@ -1733,19 +2183,11 @@ public static Object perform(VirtualFrame frame, @Operation(storeBytecodeIndex = false) public static final class MakeList { - @Specialization(guards = "elements.length == 0") - public static PList doEmpty(@Variadic Object[] elements, - @Bind PBytecodeDSLRootNode rootNode) { - // Common pattern is to create an empty list and then add items. - // We need to start from empty storage, so that we can specialize to, say, int storage - // if only ints are appended to this list - return PFactory.createList(rootNode.getLanguage(), EmptySequenceStorage.INSTANCE); - } - - @Specialization(guards = "elements.length > 0") + @Specialization public static PList perform(@Variadic Object[] elements, - @Bind PBytecodeDSLRootNode rootNode) { - return PFactory.createList(rootNode.getLanguage(), elements); + @Bind PBytecodeDSLRootNode rootNode, + @Cached SequenceFromArrayNode.ListFromArrayNode listFromArrayNode) { + return listFromArrayNode.execute(rootNode.getLanguage(), elements); } } @@ -1792,8 +2234,9 @@ public static PFrozenSet doNonEmpty(VirtualFrame frame, @Variadic Object[] eleme public static final class MakeTuple { @Specialization public static Object perform(@Variadic Object[] elements, - @Bind PBytecodeDSLRootNode rootNode) { - return PFactory.createTuple(rootNode.getLanguage(), elements); + @Bind PBytecodeDSLRootNode rootNode, + @Cached SequenceFromArrayNode.TupleFromArrayNode tupleFromArrayNode) { + return tupleFromArrayNode.execute(rootNode.getLanguage(), elements); } } @@ -1912,7 +2355,17 @@ public static final class MakeConstantObjectTuple { @Specialization public static PTuple perform(Object[] array, @Bind PBytecodeDSLRootNode rootNode) { - SequenceStorage storage = new ObjectSequenceStorage(array); + SequenceStorage storage; + if (rootNode.getLanguage().isSingleContext()) { + storage = new ObjectSequenceStorage(array, array.length); + } else { + /* + * In multi-context mode, even if we started with context-independent objects, they can get replaced with + * context-dependent ones when using the C API (i.e. TruffleString gets "inflated" to PString). + * So we need to copy. + */ + storage = new ObjectSequenceStorage(PythonUtils.arrayCopyOf(array, array.length)); + } return PFactory.createTuple(rootNode.getLanguage(), storage); } } @@ -1998,15 +2451,15 @@ public static PDict empty(VirtualFrame frame, int entries, @Variadic Object[] ke if (keysAndValues.length != entries * 2) { throw CompilerDirectives.shouldNotReachHere(); } - ObjectHashMap map = new ObjectHashMap(keysAndValues.length / 2); - PDict dict = PFactory.createDict(rootNode.getLanguage(), new EconomicMapStorage(map, false)); + EconomicMapStorage map = EconomicMapStorage.create(entries); + PDict dict = PFactory.createDict(rootNode.getLanguage(), map); for (int i = 0; i < entries; i++) { Object key = keysAndValues[i * 2]; Object value = keysAndValues[i * 2 + 1]; // Each entry represents either a k: v pair or a **splats. splats have no key. if (key == PNone.NO_VALUE) { updateNode.execute(frame, dict, value); - assert dict.getDictStorage() instanceof EconomicMapStorage es && es.mapIsEqualTo(map); + assert dict.getDictStorage() == map; } else { long hash = hashNode.execute(frame, inliningTarget, key); putNode.put(frame, inliningTarget, map, key, hash, value); @@ -3103,7 +3556,7 @@ public static Object doCall(VirtualFrame frame, Object callable, Object[] args, @Operation(storeBytecodeIndex = true) @ImportStatic(CallDispatchers.class) public static final class CallComprehension { - @Specialization + @Specialization(excludeForUncached = true) public static Object doObject(VirtualFrame frame, PFunction callable, Object arg, @Bind Node inliningTarget, @Cached("createDirectCallNodeFor(callable)") DirectCallNode callNode, @@ -3112,6 +3565,14 @@ public static Object doObject(VirtualFrame frame, PFunction callable, Object arg args[PArguments.USER_ARGUMENTS_OFFSET] = arg; return invoke.execute(frame, inliningTarget, callNode, callable, args); } + + @Specialization(replaces = "doObject") + @InliningCutoff + public static Object doObjectUncached(VirtualFrame frame, PFunction callable, Object arg) { + Object[] args = PArguments.create(1); + args[PArguments.USER_ARGUMENTS_OFFSET] = arg; + return FunctionIndirectInvokeNode.getUncached().execute(frame, null, callable, args); + } } @Operation(storeBytecodeIndex = true) @@ -3433,7 +3894,7 @@ public static final class PreResumeYield { @Specialization public static Object doObject(VirtualFrame frame, LocalAccessor currentGeneratorException, LocalAccessor savedException, Object sendValue, @Bind BytecodeNode bytecode) { - if (savedException != currentGeneratorException) { + if (!savedException.equals(currentGeneratorException)) { // We cannot pass `null` as savedException, so savedException == // currentGeneratorException means "no saveException" // @@ -3470,7 +3931,7 @@ public static Object doObject(VirtualFrame frame, Object sendValue, @Bind PBytecodeDSLRootNode root, @Bind BytecodeNode bytecode, @Bind("$bytecodeIndex") int bci) { - root.resetInstrumenationData(frame, bytecode); + root.resetInstrumentationDataForResume(frame, bytecode, bci); root.traceOrProfileCall(frame, bytecode, bci); return sendValue; } @@ -3488,13 +3949,14 @@ public static Object doObject(Object sendValue, } } + /** Used in implementation of {@code yield from} */ @Operation(storeBytecodeIndex = true) @ConstantOperand(type = LocalAccessor.class) @ConstantOperand(type = LocalAccessor.class) public static final class YieldFromSend { private static final TruffleString T_SEND = tsLiteral("send"); - @Specialization + @Specialization(excludeForUncached = true) static boolean doGenerator(VirtualFrame virtualFrame, LocalAccessor yieldedValue, LocalAccessor returnedValue, @@ -3504,7 +3966,7 @@ static boolean doGenerator(VirtualFrame virtualFrame, @Bind BytecodeNode bytecode, @Cached CommonGeneratorBuiltins.SendNode sendNode, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { try { Object value = sendNode.execute(virtualFrame, generator, arg); yieldedValue.setObject(bytecode, virtualFrame, value); @@ -3532,7 +3994,7 @@ static boolean doIterator(VirtualFrame virtualFrame, @Cached CallSlotTpIterNextNode callIterNext, @Exclusive @Cached InlinedBranchProfile exhaustedNoException, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { try { Object value = callIterNext.execute(virtualFrame, inliningTarget, slots.tp_iternext(), iter); yieldedValue.setObject(bytecode, virtualFrame, value); @@ -3558,7 +4020,7 @@ static boolean doOther(VirtualFrame virtualFrame, @Bind("$bytecodeIndex") int bci, @Cached PyObjectCallMethodObjArgs callMethodNode, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { try { Object value = callMethodNode.execute(virtualFrame, inliningTarget, obj, T_SEND, arg); yieldedValue.setObject(bytecode, virtualFrame, value); @@ -3571,14 +4033,15 @@ static boolean doOther(VirtualFrame virtualFrame, private static void handleException(VirtualFrame frame, PException e, Node inliningTarget, BytecodeNode bytecode, IsBuiltinObjectProfile stopIterationProfile, - StopIterationBuiltins.StopIterationValueNode getValue, + PyGenFetchStopIterationValue getValue, LocalAccessor returnedValue) { e.expectStopIteration(inliningTarget, stopIterationProfile); - returnedValue.setObject(bytecode, frame, getValue.execute((PBaseException) e.getUnreifiedException())); + returnedValue.setObject(bytecode, frame, getValue.execute(inliningTarget, (PBaseException) e.getUnreifiedException())); } } + /** used in the implementation of {@code yield from} */ @Operation(storeBytecodeIndex = true) @ConstantOperand(type = LocalAccessor.class) @ConstantOperand(type = LocalAccessor.class) @@ -3587,7 +4050,7 @@ public static final class YieldFromThrow { private static final TruffleString T_CLOSE = tsLiteral("close"); private static final TruffleString T_THROW = tsLiteral("throw"); - @Specialization + @Specialization(excludeForUncached = true) static boolean doGenerator(VirtualFrame frame, LocalAccessor yieldedValue, LocalAccessor returnedValue, @@ -3599,7 +4062,7 @@ static boolean doGenerator(VirtualFrame frame, @Cached CommonGeneratorBuiltins.CloseNode closeNode, @Exclusive @Cached IsBuiltinObjectProfile profileExit, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { if (profileExit.profileException(inliningTarget, exception, GeneratorExit)) { closeNode.execute(frame, generator); throw exception; @@ -3630,7 +4093,7 @@ static boolean doOther(VirtualFrame frame, @Cached WriteUnraisableNode writeUnraisableNode, @Exclusive @Cached IsBuiltinObjectProfile profileExit, @Exclusive @Cached IsBuiltinObjectProfile stopIterationProfile, - @Exclusive @Cached StopIterationBuiltins.StopIterationValueNode getValue) { + @Exclusive @Cached PyGenFetchStopIterationValue getValue) { PException pException = (PException) exception; if (profileExit.profileException(inliningTarget, pException, GeneratorExit)) { Object close = PNone.NO_VALUE; @@ -3660,10 +4123,10 @@ static boolean doOther(VirtualFrame frame, } private static void handleException(VirtualFrame frame, PException e, Node inliningTarget, BytecodeNode bytecode, - IsBuiltinObjectProfile stopIterationProfile, StopIterationBuiltins.StopIterationValueNode getValue, + IsBuiltinObjectProfile stopIterationProfile, PyGenFetchStopIterationValue getValue, LocalAccessor returnedValue) { e.expectStopIteration(inliningTarget, stopIterationProfile); - returnedValue.setObject(bytecode, frame, getValue.execute((PBaseException) e.getUnreifiedException())); + returnedValue.setObject(bytecode, frame, getValue.execute(inliningTarget, (PBaseException) e.getUnreifiedException())); } } @@ -3881,7 +4344,7 @@ static Object execute(VirtualFrame frame, } @ImportStatic(PGuards.class) - @Operation(storeBytecodeIndex = true) + @Operation(storeBytecodeIndex = true, forceCached = true) @GenerateInline(false) @ConstantOperand(type = LocalAccessor.class) @ConstantOperand(type = LocalAccessor.class) @@ -3889,7 +4352,8 @@ public static final class SplitExceptionGroups { @TruffleBoundary private static void raiseIfNoExceptionTuples(Node inliningTarget, Object clause, ValidExceptionNode isValidException, IsSubtypeNode isSubtypeNode, SequenceStorageNodes.GetItemScalarNode getItemNode) { - if (clause instanceof PTuple clauseTuple) { + if (PyTupleCheckNode.executeUncached(clause)) { + PTuple clauseTuple = ConstructTupleNode.getUncached().execute(null, clause); SequenceStorage storage = clauseTuple.getSequenceStorage(); int length = storage.length(); for (int i = 0; i < length; i++) { @@ -3902,7 +4366,7 @@ private static void raiseIfNoExceptionTuples(Node inliningTarget, Object clause, @TruffleBoundary private static void raiseIfNoException(Node inliningTarget, Object clause, ValidExceptionNode isValidException, IsSubtypeNode isSubtypeNode, SequenceStorageNodes.GetItemScalarNode getItemNode) { - if (clause instanceof PTuple) { + if (PyTupleCheckNode.executeUncached(clause)) { raiseIfNoExceptionTuples(inliningTarget, clause, isValidException, isSubtypeNode, getItemNode); } else { if (!isValidException.execute(clause)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/SequenceFromArrayNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/SequenceFromArrayNode.java new file mode 100644 index 0000000000..607266d01c --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/SequenceFromArrayNode.java @@ -0,0 +1,277 @@ +/* + * 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 + * + * 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.nodes.bytecode_dsl; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.builtins.objects.list.PList; +import com.oracle.graal.python.builtins.objects.list.PList.ListOrigin; +import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.PythonOptions; +import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.graal.python.runtime.sequence.storage.ArrayBasedSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.BoolSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.DoubleSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.EmptySequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.IntSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.LongSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage.StorageType; +import com.oracle.graal.python.runtime.sequence.storage.SequenceStorageFactory; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; +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.nodes.SlowPathException; +import com.oracle.truffle.api.profiles.InlinedIntValueProfile; +import com.oracle.truffle.api.source.SourceSection; + +abstract class SequenceFromArrayNode extends Node { + private static final SlowPathException SLOW_PATH_EXCEPTION = new SlowPathException(); + @CompilationFinal protected SequenceStorage.StorageType type = StorageType.Uninitialized; + + SequenceStorage createSequenceStorage(Object[] objectElements, int length) { + SequenceStorage storage; + if (type == SequenceStorage.StorageType.Uninitialized) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + storage = initialize(objectElements); + } else { + try { + switch (type) { + // Ugh. We want to use primitive arrays during unpacking, so + // we cannot dispatch generically here. + case Empty: { + if (length != 0) { + throw SLOW_PATH_EXCEPTION; + } + storage = EmptySequenceStorage.INSTANCE; + break; + } + case Boolean: { + boolean[] elements = new boolean[getCapacityEstimate(length)]; + for (int i = 0; i < length; i++) { + elements[i] = castBoolean(objectElements[i]); + } + storage = new BoolSequenceStorage(elements, length); + break; + } + case Int: { + int[] elements = new int[getCapacityEstimate(length)]; + for (int i = 0; i < length; i++) { + elements[i] = castInt(objectElements[i]); + } + storage = new IntSequenceStorage(elements, length); + break; + } + case Long: { + long[] elements = new long[getCapacityEstimate(length)]; + for (int i = 0; i < length; i++) { + elements[i] = castLong(objectElements[i]); + } + storage = new LongSequenceStorage(elements, length); + break; + } + case Double: { + double[] elements = new double[getCapacityEstimate(length)]; + for (int i = 0; i < length; i++) { + elements[i] = castDouble(objectElements[i]); + } + storage = new DoubleSequenceStorage(elements, length); + break; + } + case Generic: { + storage = new ObjectSequenceStorage(objectElements, length); + break; + } + default: + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new RuntimeException("unexpected state"); + } + } catch (SlowPathException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + type = SequenceStorage.StorageType.Generic; + storage = new ObjectSequenceStorage(objectElements, length); + } + } + return storage; + } + + @InliningCutoff + private SequenceStorage initialize(Object[] objectElements) { + SequenceStorage storage; + try { + storage = SequenceStorageFactory.createStorage(objectElements); + type = storage.getElementType(); + } catch (Throwable t) { + // we do not want to repeatedly deopt if a value execution + // always raises, for example + type = SequenceStorage.StorageType.Generic; + throw t; + } + return storage; + } + + private static int castInt(Object o) throws SlowPathException { + if (o instanceof Integer) { + return (int) o; + } + throw SLOW_PATH_EXCEPTION; + } + + private static long castLong(Object o) throws SlowPathException { + if (o instanceof Long) { + return (long) o; + } + throw SLOW_PATH_EXCEPTION; + } + + private static double castDouble(Object o) throws SlowPathException { + if (o instanceof Double) { + return (double) o; + } + throw SLOW_PATH_EXCEPTION; + } + + private static boolean castBoolean(Object o) throws SlowPathException { + if (o instanceof Boolean) { + return (boolean) o; + } + throw SLOW_PATH_EXCEPTION; + } + + protected abstract int getCapacityEstimate(int length); + + public abstract static class ListFromArrayNode extends SequenceFromArrayNode implements ListOrigin { + private static final TruffleLogger LOGGER = PythonLanguage.getLogger(ListFromArrayNode.class); + private static final ListFromArrayNode UNCACHED = new ListFromArrayNode() { + @Override + public PList execute(PythonLanguage language, Object[] elements) { + return PFactory.createList(language, SequenceStorageFactory.createStorage(elements)); + } + }; + + public static ListFromArrayNode getUncached() { + return UNCACHED; + } + + @CompilationFinal private SizeEstimate initialCapacity; + + public abstract PList execute(PythonLanguage language, Object[] elements); + + @Specialization + PList doIt(PythonLanguage language, Object[] elements, + @Bind Node inliningTarget, + @Cached InlinedIntValueProfile lengthProfile) { + SequenceStorage storage = createSequenceStorage(elements, lengthProfile.profile(inliningTarget, elements.length)); + return PFactory.createList(language, storage, initialCapacity != null ? this : null); + } + + @Override + protected int getCapacityEstimate(int length) { + assert isAdoptable(); + if (initialCapacity == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + initialCapacity = new SizeEstimate(length); + } + return Math.max(initialCapacity.estimate(), length); + } + + @Override + public void reportUpdatedCapacity(ArrayBasedSequenceStorage newStore) { + if (CompilerDirectives.inInterpreter()) { + if (PythonContext.get(this).getOption(PythonOptions.OverallocateLiteralLists)) { + if (newStore.getCapacity() > initialCapacity.estimate()) { + initialCapacity.updateFrom(newStore.getCapacity()); + LOGGER.finest(() -> { + SourceSection encapsulatingSourceSection = getEncapsulatingSourceSection(); + String sourceSection = encapsulatingSourceSection == null ? "" : encapsulatingSourceSection.toString(); + return String.format("Updating list size estimate at %s. Observed capacity: %d, new estimate: %d", sourceSection, newStore.getCapacity(), + initialCapacity.estimate()); + }); + } + if (newStore.getElementType().generalizesFrom(type)) { + type = newStore.getElementType(); + LOGGER.finest(() -> { + SourceSection encapsulatingSourceSection = getEncapsulatingSourceSection(); + String sourceSection = encapsulatingSourceSection == null ? "" : encapsulatingSourceSection.toString(); + return String.format("Updating list type estimate at %s. New type: %s", sourceSection, type.name()); + }); + } + } + } + // n.b.: it's ok that this races when the code is already being compiled + // or if we're running on multiple threads. if the update isn't seen, we + // are not incorrect, we just don't benefit from the optimization + } + } + + public abstract static class TupleFromArrayNode extends SequenceFromArrayNode { + private static final TupleFromArrayNode UNCACHED = new TupleFromArrayNode() { + @Override + public PTuple execute(PythonLanguage language, Object[] elements) { + return PFactory.createTuple(language, SequenceStorageFactory.createStorage(elements)); + } + }; + + public static TupleFromArrayNode getUncached() { + return UNCACHED; + } + + public abstract PTuple execute(PythonLanguage language, Object[] elements); + + @Specialization + PTuple doIt(PythonLanguage language, Object[] elements, + @Bind Node inliningTarget, + @Cached InlinedIntValueProfile lengthProfile) { + return PFactory.createTuple(language, createSequenceStorage(elements, lengthProfile.profile(inliningTarget, elements.length))); + } + + @Override + protected int getCapacityEstimate(int length) { + return length; + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/CallDispatchers.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/CallDispatchers.java index 6c2ca74ccd..796808c5e5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/CallDispatchers.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/CallDispatchers.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 @@ -50,7 +50,7 @@ import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.argument.CreateArgumentsNode; -import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode; +import com.oracle.graal.python.nodes.call.CallDispatchersFactory.FunctionIndirectInvokeNodeGen; import com.oracle.graal.python.runtime.ExecutionContext; import com.oracle.graal.python.runtime.ExecutionContext.IndirectCalleeContext; import com.oracle.graal.python.runtime.PythonContext; @@ -75,6 +75,7 @@ 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.RootNode; import com.oracle.truffle.api.profiles.InlinedConditionProfile; public class CallDispatchers { @@ -82,8 +83,7 @@ public class CallDispatchers { @NeverDefault public static DirectCallNode createDirectCallNodeFor(PBuiltinFunction callee) { DirectCallNode callNode = Truffle.getRuntime().createDirectCallNode(callee.getCallTarget()); - if (PythonLanguage.get(null).getEngineOption(PythonOptions.EnableForcedSplits) || - (callee.getFunctionRootNode() instanceof BuiltinFunctionRootNode root && root.getBuiltin().forceSplitDirectCalls())) { + if (PythonLanguage.get(null).getEngineOption(PythonOptions.EnableForcedSplits) || callee.forceSplitDirectCalls()) { callNode.cloneCallTarget(); } return callNode; @@ -193,8 +193,9 @@ static Object callBuiltinFunctionCached(VirtualFrame frame, Node inliningTarget, return invoke.execute(frame, inliningTarget, callNode, arguments); } - @Specialization(guards = "sameCallTarget(callee.getCallTarget(), callNode)", limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callBuiltinFunctionCached") + @Specialization(guards = "callee.getFunctionRootNode() == rootNode", limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callBuiltinFunctionCached") static Object callBuiltinFunctionCachedCt(VirtualFrame frame, Node inliningTarget, @SuppressWarnings("unused") PBuiltinFunction callee, Object[] arguments, + @SuppressWarnings("unused") @Cached(value = "callee.getFunctionRootNode()", adopt = false) RootNode rootNode, @Cached("createDirectCallNodeFor(callee)") DirectCallNode callNode, @Exclusive @Cached SimpleDirectInvokeNode invoke) { return invoke.execute(frame, inliningTarget, callNode, arguments); @@ -232,8 +233,9 @@ static Object callBuiltinFunctionCached(VirtualFrame frame, Node inliningTarget, return invoke.execute(frame, inliningTarget, callNode, pArguments); } - @Specialization(guards = "sameCallTarget(callee.getCallTarget(), callNode)", limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callBuiltinFunctionCached") + @Specialization(guards = "callee.getFunctionRootNode() == rootNode", limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callBuiltinFunctionCached") static Object callBuiltinFunctionCachedCt(VirtualFrame frame, Node inliningTarget, PBuiltinFunction callee, Object[] arguments, PKeyword[] keywords, + @SuppressWarnings("unused") @Cached(value = "callee.getFunctionRootNode()", adopt = false) RootNode rootNode, @Exclusive @Cached CreateArgumentsNode createArgs, @Cached("createDirectCallNodeFor(callee)") DirectCallNode callNode, @Exclusive @Cached SimpleDirectInvokeNode invoke) { @@ -288,9 +290,10 @@ static Object callBuiltinMethodCached(VirtualFrame frame, Node inliningTarget, @ return invoke.execute(frame, inliningTarget, callNode, pArguments); } - @Specialization(guards = "sameCallTarget(function.getCallTarget(), callNode)", limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callBuiltinMethodCached") + @Specialization(guards = "function.getFunctionRootNode() == rootNode", limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callBuiltinMethodCached") static Object callBuiltinMethodCachedCt(VirtualFrame frame, Node inliningTarget, PBuiltinMethod callee, Object[] arguments, PKeyword[] keywords, @Bind("callee.getBuiltinFunction()") PBuiltinFunction function, + @SuppressWarnings("unused") @Cached(value = "function.getFunctionRootNode()", adopt = false) RootNode rootNode, @Exclusive @Cached CreateArgumentsNode createArgs, @Cached("createDirectCallNodeFor(function)") DirectCallNode callNode, @Exclusive @Cached SimpleDirectInvokeNode invoke) { @@ -355,6 +358,10 @@ static Object doDirect(VirtualFrame frame, Node inliningTarget, DirectCallNode c @GenerateUncached public abstract static class FunctionIndirectInvokeNode extends Node { + public static FunctionIndirectInvokeNode getUncached() { + return FunctionIndirectInvokeNodeGen.getUncached(); + } + public abstract Object execute(Frame frame, Node inliningTarget, PFunction callee, Object[] arguments); @Specialization @@ -387,8 +394,9 @@ static Object callFunctionCached(VirtualFrame frame, Node inliningTarget, @Suppr } // We have multiple contexts, don't cache the objects so that contexts can be cleaned up - @Specialization(guards = {"sameCallTarget(callee.getCallTarget(), callNode)"}, limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callFunctionCached") + @Specialization(guards = {"callee.getCode().getRootNode() == rootNode"}, limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callFunctionCached") static Object callFunctionCachedCt(VirtualFrame frame, Node inliningTarget, PFunction callee, Object[] arguments, + @SuppressWarnings("unused") @Cached(value = "callee.getCode().getRootNode()", adopt = false) RootNode rootNode, @Cached("createDirectCallNodeFor(callee)") DirectCallNode callNode, @Exclusive @Cached FunctionDirectInvokeNode invoke) { return invoke.execute(frame, inliningTarget, callNode, callee, arguments); @@ -405,7 +413,7 @@ static Object callFunctionMegamorphic(VirtualFrame frame, Node inliningTarget, P /** * Node for calling python functions with an inline cache on the function object and a secondary - * inline cache on the call target. Takes PArguments + * inline cache on the call target. */ @GenerateInline @GenerateCached(false) @@ -427,8 +435,9 @@ static Object callFunctionCached(VirtualFrame frame, Node inliningTarget, @Suppr } // We have multiple contexts, don't cache the objects so that contexts can be cleaned up - @Specialization(guards = {"sameCallTarget(callee.getCallTarget(), callNode)"}, limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callFunctionCached") + @Specialization(guards = {"callee.getCode().getRootNode() == rootNode"}, limit = "getCallSiteInlineCacheMaxDepth()", replaces = "callFunctionCached") static Object callFunctionCachedCt(VirtualFrame frame, Node inliningTarget, PFunction callee, Object[] arguments, PKeyword[] keywords, + @SuppressWarnings("unused") @Cached(value = "callee.getCode().getRootNode()", adopt = false) RootNode rootNode, @Exclusive @Cached CreateArgumentsNode createArgs, @Cached("createDirectCallNodeFor(callee)") DirectCallNode callNode, @Exclusive @Cached FunctionDirectInvokeNode invoke) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/AbstractCallMethodNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/AbstractCallMethodNode.java index e85f993f7a..e181ef1826 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/AbstractCallMethodNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/AbstractCallMethodNode.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,35 +40,26 @@ */ package com.oracle.graal.python.nodes.call.special; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Slot.SlotSignature; import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.objects.PNone; 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.method.PMethod; import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotBuiltin; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.nodes.PRootNode; -import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetCallTargetNode; -import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonQuaternaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.runtime.PythonOptions; -import com.oracle.graal.python.util.PythonUtils.NodeCounterWithLimit; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.NodeField; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; @ImportStatic({PythonOptions.class, PGuards.class}) @NodeField(name = "maxSizeExceeded", type = boolean.class) @@ -142,77 +133,25 @@ private static int getBuiltinNodeArity(Class no } private boolean callerExceedsMaxSize(T builtinNode) { - CompilerAsserts.neverPartOfCompilation(); - if (isAdoptable() && !isMaxSizeExceeded()) { - // to avoid building up AST of recursive builtin calls we check that the same builtin - // isn't already our parent. - Class builtinClass = builtinNode.getClass(); - Node parent = getParent(); - int recursiveCalls = 0; - PythonLanguage language = PythonLanguage.get(this); - while (parent != null && !(parent instanceof RootNode)) { - if (parent.getClass() == builtinClass) { - int recursionLimit = language.getEngineOption(PythonOptions.NodeRecursionLimit); - if (recursiveCalls == recursionLimit) { - return true; - } - recursiveCalls++; - } - parent = parent.getParent(); - } - - RootNode root = getRootNode(); - // nb: option 'BuiltinsInliningMaxCallerSize' is defined as a compatible option, i.e., - // ASTs will only be shared between contexts that have the same value for this option. - int maxSize = language.getEngineOption(PythonOptions.BuiltinsInliningMaxCallerSize); - if (root instanceof PRootNode) { - PRootNode pRoot = (PRootNode) root; - int rootNodeCount = pRoot.getNodeCountForInlining(); - if (rootNodeCount < maxSize) { - NodeCounterWithLimit counter = new NodeCounterWithLimit(rootNodeCount, maxSize); - builtinNode.accept(counter); - if (counter.isOverLimit()) { - setMaxSizeExceeded(true); - return true; - } - pRoot.setNodeCountForInlining(counter.getCount()); - } - } else { - NodeCounterWithLimit counter = new NodeCounterWithLimit(maxSize); - root.accept(counter); - if (!counter.isOverLimit()) { - builtinNode.accept(counter); - } - if (counter.isOverLimit()) { - setMaxSizeExceeded(true); - return true; - } + if (!isMaxSizeExceeded()) { + BuiltinInliningPolicy.CallerSizeCheck result = BuiltinInliningPolicy.checkCallerSize(this, builtinNode); + if (result == BuiltinInliningPolicy.CallerSizeCheck.EXCEEDS_MAX_SIZE) { + setMaxSizeExceeded(true); } - return false; + return BuiltinInliningPolicy.exceedsCallerSize(result); } return true; } protected static boolean takesSelfArg(Object func) { if (func instanceof PBuiltinFunction) { - RootNode functionRootNode = ((PBuiltinFunction) func).getFunctionRootNode(); - if (functionRootNode instanceof BuiltinFunctionRootNode) { - return ((BuiltinFunctionRootNode) functionRootNode).declaresExplicitSelf(); - } + return ((PBuiltinFunction) func).declaresExplicitSelf(); } else if (func instanceof PBuiltinMethod) { return takesSelfArg(((PBuiltinMethod) func).getFunction()); } return true; } - protected static RootCallTarget getCallTarget(PMethod meth, GetCallTargetNode getCtNode) { - return getCtNode.execute(meth.getFunction()); - } - - protected static RootCallTarget getCallTarget(PBuiltinMethod meth, GetCallTargetNode getCtNode) { - return getCtNode.execute(meth.getFunction()); - } - protected static Object callUnaryBuiltin(VirtualFrame frame, PythonBuiltinBaseNode builtin, Object arg1) { CompilerAsserts.partialEvaluationConstant(builtin); if (builtin instanceof PythonUnaryBuiltinNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/BuiltinInliningPolicy.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/BuiltinInliningPolicy.java new file mode 100644 index 0000000000..13ca02a8eb --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/BuiltinInliningPolicy.java @@ -0,0 +1,116 @@ +/* + * 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.nodes.call.special; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.nodes.PRootNode; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; +import com.oracle.graal.python.runtime.PythonOptions; +import com.oracle.graal.python.util.PythonUtils.NodeCounterWithLimit; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; + +final class BuiltinInliningPolicy { + + enum CallerSizeCheck { + OK, + EXCEEDS_MAX_SIZE, + DISABLED + } + + private BuiltinInliningPolicy() { + } + + static CallerSizeCheck checkCallerSize(Node caller, T builtinNode) { + CompilerAsserts.neverPartOfCompilation(); + if (caller.isAdoptable()) { + // To avoid building up ASTs of recursive builtin calls, check that the same builtin + // isn't already the call node's parent. + Class builtinClass = builtinNode.getClass(); + Node parent = caller.getParent(); + int recursiveCalls = 0; + PythonLanguage language = PythonLanguage.get(caller); + while (parent != null && !(parent instanceof RootNode)) { + if (parent.getClass() == builtinClass) { + int recursionLimit = language.getEngineOption(PythonOptions.NodeRecursionLimit); + if (recursiveCalls == recursionLimit) { + return CallerSizeCheck.DISABLED; + } + recursiveCalls++; + } + parent = parent.getParent(); + } + + RootNode root = caller.getRootNode(); + // nb: option 'BuiltinsInliningMaxCallerSize' is defined as a compatible option, i.e., + // ASTs will only be shared between contexts that have the same value for this option. + int maxSize = language.getEngineOption(PythonOptions.BuiltinsInliningMaxCallerSize); + if (root instanceof PRootNode) { + PRootNode pRoot = (PRootNode) root; + int rootNodeCount = pRoot.getNodeCountForInlining(); + if (rootNodeCount < maxSize) { + NodeCounterWithLimit counter = new NodeCounterWithLimit(rootNodeCount, maxSize); + builtinNode.accept(counter); + if (counter.isOverLimit()) { + return CallerSizeCheck.EXCEEDS_MAX_SIZE; + } + pRoot.setNodeCountForInlining(counter.getCount()); + } + } else { + NodeCounterWithLimit counter = new NodeCounterWithLimit(maxSize); + root.accept(counter); + if (!counter.isOverLimit()) { + builtinNode.accept(counter); + } + if (counter.isOverLimit()) { + return CallerSizeCheck.EXCEEDS_MAX_SIZE; + } + } + return CallerSizeCheck.OK; + } + return CallerSizeCheck.DISABLED; + } + + static boolean exceedsCallerSize(CallerSizeCheck result) { + return result != CallerSizeCheck.OK; + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallBinaryMethodNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallBinaryMethodNode.java index 220e12751f..97183b4883 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallBinaryMethodNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallBinaryMethodNode.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 @@ -43,16 +43,13 @@ 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.nodes.builtins.FunctionNodes.GetCallTargetNode; import com.oracle.graal.python.nodes.call.BoundDescriptor; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; -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.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.NeverDefault; @@ -61,6 +58,7 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; 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.InlinedConditionProfile; @@ -95,10 +93,10 @@ static Object callObjectSingleContext(VirtualFrame frame, @SuppressWarnings("unu return callBinaryBuiltin(frame, builtinNode, arg1, arg2); } - @Specialization(guards = {"func.getCallTarget() == ct", "builtinNode != null"}, // + @Specialization(guards = {"func.getFunctionRootNode() == rootNode", "builtinNode != null"}, // limit = "getCallSiteInlineCacheMaxDepth()") static Object callObject(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinFunction func, Object arg1, Object arg2, - @SuppressWarnings("unused") @Cached("func.getCallTarget()") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getFunctionRootNode()", adopt = false) RootNode rootNode, @Cached("getBuiltin(frame, func, 2)") PythonBuiltinBaseNode builtinNode) { return callBinaryBuiltin(frame, builtinNode, arg1, arg2); } @@ -111,10 +109,9 @@ static Object callMethodSingleContext(VirtualFrame frame, @SuppressWarnings("unu return callBinaryBuiltin(frame, builtinNode, arg1, arg2); } - @Specialization(guards = {"builtinNode != null", "getCallTarget(func, getCt) == ct", "!takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"builtinNode != null", "func.getBuiltinFunction().getFunctionRootNode() == rootNode", "!takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") static Object callMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2, - @SuppressWarnings("unused") @Shared @Cached GetCallTargetNode getCt, - @SuppressWarnings("unused") @Cached("getCallTarget(func, getCt)") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getBuiltinFunction().getFunctionRootNode()", adopt = false) RootNode rootNode, @SuppressWarnings("unused") @Cached("takesSelfArg(func)") boolean takesSelfArg, @Cached("getBuiltin(frame, func.getBuiltinFunction(), 2)") PythonBuiltinBaseNode builtinNode) { return callBinaryBuiltin(frame, builtinNode, arg1, arg2); @@ -128,10 +125,9 @@ static Object callMethodSingleContextSelf(VirtualFrame frame, @SuppressWarnings( return callTernaryBuiltin(frame, builtinNode, cachedFunc.getSelf(), arg1, arg2); } - @Specialization(guards = {"builtinNode != null", "getCallTarget(func, getCt) == ct", "takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"builtinNode != null", "func.getBuiltinFunction().getFunctionRootNode() == rootNode", "takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") static Object callMethodSelf(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2, - @SuppressWarnings("unused") @Shared @Cached GetCallTargetNode getCt, - @SuppressWarnings("unused") @Cached("getCallTarget(func, getCt)") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getBuiltinFunction().getFunctionRootNode()", adopt = false) RootNode rootNode, @SuppressWarnings("unused") @Cached("takesSelfArg(func)") boolean takesSelfArg, @Cached("getBuiltin(frame, func.getBuiltinFunction(), 3)") PythonBuiltinBaseNode builtinNode) { return callTernaryBuiltin(frame, builtinNode, func.getSelf(), arg1, arg2); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallQuaternaryMethodNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallQuaternaryMethodNode.java index 89e8ce6ec6..d985582d1e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallQuaternaryMethodNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallQuaternaryMethodNode.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 @@ -43,12 +43,10 @@ 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.nodes.builtins.FunctionNodes.GetCallTargetNode; import com.oracle.graal.python.nodes.call.BoundDescriptor; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; -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.GenerateUncached; @@ -57,6 +55,7 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.InlinedConditionProfile; @GenerateUncached @@ -80,10 +79,10 @@ Object callSingle(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinFuncti return callQuaternaryBuiltin(frame, builtinNode, arg1, arg2, arg3, arg4); } - @Specialization(guards = {"func.getCallTarget() == ct", "builtinNode != null"}, // + @Specialization(guards = {"func.getFunctionRootNode() == rootNode", "builtinNode != null"}, // limit = "getCallSiteInlineCacheMaxDepth()") Object call(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinFunction func, Object arg1, Object arg2, Object arg3, Object arg4, - @SuppressWarnings("unused") @Cached("func.getCallTarget()") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getFunctionRootNode()", adopt = false) RootNode rootNode, @Cached("getBuiltin(frame, func, 4)") PythonBuiltinBaseNode builtinNode) { return callQuaternaryBuiltin(frame, builtinNode, arg1, arg2, arg3, arg4); } @@ -96,10 +95,9 @@ Object callMethodSingle(VirtualFrame frame, @SuppressWarnings("unused") PBuiltin return callQuaternaryBuiltin(frame, builtinNode, arg1, arg2, arg3, arg4); } - @Specialization(guards = {"builtinNode != null", "getCallTarget(func, getCt) == ct", "!takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"builtinNode != null", "func.getBuiltinFunction().getFunctionRootNode() == rootNode", "!takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") Object callMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2, Object arg3, Object arg4, - @SuppressWarnings("unused") @Cached GetCallTargetNode getCt, - @SuppressWarnings("unused") @Cached("getCallTarget(func, getCt)") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getBuiltinFunction().getFunctionRootNode()", adopt = false) RootNode rootNode, @SuppressWarnings("unused") @Cached("takesSelfArg(func)") boolean takesSelfArg, @Cached("getBuiltin(frame, func.getBuiltinFunction(), 4)") PythonBuiltinBaseNode builtinNode) { return callQuaternaryBuiltin(frame, builtinNode, arg1, arg2, arg3, arg4); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallTernaryMethodNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallTernaryMethodNode.java index f0ac256134..022e23a351 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallTernaryMethodNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallTernaryMethodNode.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 @@ -43,15 +43,12 @@ 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.nodes.builtins.FunctionNodes.GetCallTargetNode; import com.oracle.graal.python.nodes.call.BoundDescriptor; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; -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.Shared; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.ReportPolymorphism.Megamorphic; @@ -59,6 +56,7 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.InlinedConditionProfile; @GenerateUncached @@ -81,10 +79,10 @@ static Object doBuiltinFunctionCached(VirtualFrame frame, @SuppressWarnings("unu return callTernaryBuiltin(frame, builtinNode, arg1, arg2, arg3); } - @Specialization(guards = {"func.getCallTarget() == ct", "builtinNode != null"}, // + @Specialization(guards = {"func.getFunctionRootNode() == rootNode", "builtinNode != null"}, // limit = "getCallSiteInlineCacheMaxDepth()") static Object doBuiltinFunctionCtCached(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinFunction func, Object arg1, Object arg2, Object arg3, - @SuppressWarnings("unused") @Cached("func.getCallTarget()") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getFunctionRootNode()", adopt = false) RootNode rootNode, @Cached("getBuiltin(frame, func, 3)") PythonBuiltinBaseNode builtinNode) { return callTernaryBuiltin(frame, builtinNode, arg1, arg2, arg3); } @@ -97,10 +95,9 @@ static Object doBuiltinMethodCached(VirtualFrame frame, @SuppressWarnings("unuse return callTernaryBuiltin(frame, builtinNode, arg1, arg2, arg3); } - @Specialization(guards = {"builtinNode != null", "getCallTarget(func, getCt) == ct", "!takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"builtinNode != null", "func.getBuiltinFunction().getFunctionRootNode() == rootNode", "!takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") static Object doBuiltinMethodCtCached(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2, Object arg3, - @SuppressWarnings("unused") @Shared @Cached GetCallTargetNode getCt, - @SuppressWarnings("unused") @Cached("getCallTarget(func, getCt)") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getBuiltinFunction().getFunctionRootNode()", adopt = false) RootNode rootNode, @SuppressWarnings("unused") @Cached("takesSelfArg(func)") boolean takesSelfArg, @Cached("getBuiltin(frame, func.getBuiltinFunction(), 3)") PythonBuiltinBaseNode builtinNode) { return callTernaryBuiltin(frame, builtinNode, arg1, arg2, arg3); @@ -114,10 +111,9 @@ static Object callSelfMethodSingleContext(VirtualFrame frame, @SuppressWarnings( return callQuaternaryBuiltin(frame, builtinNode, func.getSelf(), arg1, arg2, arg3); } - @Specialization(guards = {"builtinNode != null", "getCallTarget(func, getCt) == ct", "takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"builtinNode != null", "func.getBuiltinFunction().getFunctionRootNode() == rootNode", "takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") static Object callSelfMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMethod func, Object arg1, Object arg2, Object arg3, - @SuppressWarnings("unused") @Shared @Cached GetCallTargetNode getCt, - @SuppressWarnings("unused") @Cached("getCallTarget(func, getCt)") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getBuiltinFunction().getFunctionRootNode()", adopt = false) RootNode rootNode, @SuppressWarnings("unused") @Cached("takesSelfArg(func)") boolean takesSelfArg, @Cached("getBuiltin(frame, func.getBuiltinFunction(), 4)") PythonBuiltinBaseNode builtinNode) { return callQuaternaryBuiltin(frame, builtinNode, func.getSelf(), arg1, arg2, arg3); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallUnaryMethodNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallUnaryMethodNode.java index 7430ea5264..245bda2270 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallUnaryMethodNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/CallUnaryMethodNode.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 @@ -43,17 +43,14 @@ 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.nodes.builtins.FunctionNodes.GetCallTargetNode; import com.oracle.graal.python.nodes.call.BoundDescriptor; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; -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.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.ReportPolymorphism.Megamorphic; @@ -61,6 +58,7 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.InlinedConditionProfile; @GenerateUncached @@ -89,10 +87,10 @@ Object callObjectSingle(VirtualFrame frame, @SuppressWarnings("unused") PBuiltin return callUnaryBuiltin(frame, builtinNode, receiver); } - @Specialization(guards = {"func.getCallTarget() == ct", "builtinNode != null"}, // + @Specialization(guards = {"func.getFunctionRootNode() == rootNode", "builtinNode != null"}, // limit = "getCallSiteInlineCacheMaxDepth()") Object callObject(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinFunction func, Object receiver, - @SuppressWarnings("unused") @Cached(value = "func.getCallTarget()") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getFunctionRootNode()", adopt = false) RootNode rootNode, @Cached("getBuiltin(frame, func, 1)") PythonBuiltinBaseNode builtinNode) { return callUnaryBuiltin(frame, builtinNode, receiver); } @@ -105,10 +103,9 @@ Object callMethodSingleContext(VirtualFrame frame, @SuppressWarnings("unused") P return callUnaryBuiltin(frame, builtinNode, receiver); } - @Specialization(guards = {"builtinNode != null", "getCallTarget(func, getCt) == ct", "!takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"builtinNode != null", "func.getBuiltinFunction().getFunctionRootNode() == rootNode", "!takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") Object callMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMethod func, Object receiver, - @SuppressWarnings("unused") @Shared @Cached GetCallTargetNode getCt, - @SuppressWarnings("unused") @Cached("getCallTarget(func, getCt)") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getBuiltinFunction().getFunctionRootNode()", adopt = false) RootNode rootNode, @SuppressWarnings("unused") @Cached("takesSelfArg(func)") boolean takesSelfArg, @Cached("getBuiltin(frame, func.getBuiltinFunction(), 1)") PythonBuiltinBaseNode builtinNode) { return callUnaryBuiltin(frame, builtinNode, receiver); @@ -122,10 +119,9 @@ Object callSelfMethodSingleContext(VirtualFrame frame, @SuppressWarnings("unused return callBinaryBuiltin(frame, builtinNode, func.getSelf(), arg); } - @Specialization(guards = {"builtinNode != null", "getCallTarget(func, getCt) == ct", "takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"builtinNode != null", "func.getBuiltinFunction().getFunctionRootNode() == rootNode", "takesSelfArg"}, limit = "getCallSiteInlineCacheMaxDepth()") Object callSelfMethod(VirtualFrame frame, @SuppressWarnings("unused") PBuiltinMethod func, Object arg, - @SuppressWarnings("unused") @Shared @Cached GetCallTargetNode getCt, - @SuppressWarnings("unused") @Cached("getCallTarget(func, getCt)") RootCallTarget ct, + @SuppressWarnings("unused") @Cached(value = "func.getBuiltinFunction().getFunctionRootNode()", adopt = false) RootNode rootNode, @SuppressWarnings("unused") @Cached("takesSelfArg(func)") boolean takesSelfArg, @Cached("getBuiltin(frame, func.getBuiltinFunction(), 2)") PythonBuiltinBaseNode builtinNode) { return callBinaryBuiltin(frame, builtinNode, func.getSelf(), arg); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.java index 9116057b2b..58d02f3bcd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallBinaryNode.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 @@ -44,19 +44,21 @@ import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.PythonOptions; -import com.oracle.truffle.api.CompilerDirectives; +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.dsl.Cached.Exclusive; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; -import com.oracle.truffle.api.dsl.ReportPolymorphism.Megamorphic; +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.profiles.InlinedBranchProfile; import com.oracle.truffle.api.strings.TruffleString; /** @@ -66,7 +68,6 @@ */ @ImportStatic(PythonOptions.class) public abstract class LookupAndCallBinaryNode extends Node { - @Child private CallBinaryMethodNode dispatchNode; protected final TruffleString name; LookupAndCallBinaryNode(TruffleString name) { @@ -80,26 +81,26 @@ public static LookupAndCallBinaryNode create(TruffleString name) { return LookupAndCallBinaryNodeGen.create(name); } - protected final CallBinaryMethodNode ensureDispatch() { - // this also serves as a branch profile - if (dispatchNode == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - dispatchNode = insert(CallBinaryMethodNode.create()); - } - return dispatchNode; - } - protected final PythonBinaryBuiltinNode getBinaryBuiltin(PythonBuiltinClassType clazz) { + CompilerAsserts.neverPartOfCompilation(); Object attribute = LookupAttributeInMRONode.Dynamic.getUncached().execute(clazz, name); if (attribute instanceof PBuiltinFunction) { PBuiltinFunction builtinFunction = (PBuiltinFunction) attribute; - if (PythonBinaryBuiltinNode.class.isAssignableFrom(builtinFunction.getBuiltinNodeFactory().getNodeClass())) { - return (PythonBinaryBuiltinNode) builtinFunction.getBuiltinNodeFactory().createNode(); + NodeFactory builtinNodeFactory = builtinFunction.getBuiltinNodeFactory(); + if (builtinNodeFactory != null && PythonBinaryBuiltinNode.class.isAssignableFrom(builtinNodeFactory.getNodeClass())) { + PythonBinaryBuiltinNode builtinNode = (PythonBinaryBuiltinNode) builtinNodeFactory.createNode(); + if (!callerExceedsMaxSize(builtinNode)) { + return builtinNode; + } } } return null; } + private boolean callerExceedsMaxSize(T builtinNode) { + return BuiltinInliningPolicy.exceedsCallerSize(BuiltinInliningPolicy.checkCallerSize(this, builtinNode)); + } + protected static PythonBuiltinClassType getBuiltinClass(Node inliningTarget, Object receiver, GetClassNode getClassNode) { Object clazz = getClassNode.execute(inliningTarget, receiver); return clazz instanceof PythonBuiltinClassType ? (PythonBuiltinClassType) clazz : null; @@ -111,7 +112,7 @@ protected static boolean isClazz(Node inliningTarget, PythonBuiltinClassType cla // Object, Object - @Specialization(guards = {"clazz != null", "function != null", "isClazz(inliningTarget, clazz, left, getClassNode)"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"clazz != null", "function != null", "isClazz(inliningTarget, clazz, left, getClassNode)"}, limit = "1") static Object callObjectBuiltin(VirtualFrame frame, Object left, Object right, @SuppressWarnings("unused") @Bind Node inliningTarget, @SuppressWarnings("unused") @Exclusive @Cached GetClassNode getClassNode, @@ -120,33 +121,19 @@ static Object callObjectBuiltin(VirtualFrame frame, Object left, Object right, return function.execute(frame, left, right); } - @Specialization(guards = {"left.getClass() == cachedLeftClass", "right.getClass() == cachedRightClass"}, limit = "5") - @SuppressWarnings("truffle-static-method") - Object callObjectGeneric(VirtualFrame frame, Object left, Object right, + @Specialization(replaces = "callObjectBuiltin") + static Object callObject(VirtualFrame frame, Object left, Object right, @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached("left.getClass()") Class cachedLeftClass, - @SuppressWarnings("unused") @Cached("right.getClass()") Class cachedRightClass, + @Exclusive @Cached InlinedBranchProfile notFoundProfile, @Exclusive @Cached GetClassNode getClassNode, - @Exclusive @Cached("create(name)") LookupSpecialMethodNode getattr) { - return doCallObject(frame, inliningTarget, left, right, getClassNode, getattr); - } - - @Specialization(replaces = "callObjectGeneric") - @Megamorphic - @SuppressWarnings("truffle-static-method") - Object callObjectMegamorphic(VirtualFrame frame, Object left, Object right, - @Bind Node inliningTarget, - @Exclusive @Cached GetClassNode getClassNode, - @Exclusive @Cached("create(name)") LookupSpecialMethodNode getattr) { - return doCallObject(frame, inliningTarget, left, right, getClassNode, getattr); - } - - private Object doCallObject(VirtualFrame frame, Node inliningTarget, Object left, Object right, GetClassNode getClassNode, LookupSpecialMethodNode getattr) { + @Exclusive @Cached("create(name)") LookupSpecialMethodNode getattr, + @Cached CallBinaryMethodNode dispatchNode) { Object leftClass = getClassNode.execute(inliningTarget, left); Object leftCallable = getattr.execute(frame, leftClass, left); if (PGuards.isNoValue(leftCallable)) { + notFoundProfile.enter(inliningTarget); throw SpecialMethodNotFound.INSTANCE; } - return ensureDispatch().executeObject(frame, leftCallable, left, right); + return dispatchNode.executeObject(frame, leftCallable, left, right); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.java index b666b96a97..b6c16f5c27 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/call/special/LookupAndCallUnaryNode.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,37 +46,33 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; import com.oracle.graal.python.nodes.expression.UnaryOpNode; +import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.PythonOptions; -import com.oracle.graal.python.util.Supplier; -import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerAsserts; +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.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.ReportPolymorphism.Megamorphic; +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.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; @ImportStatic(PythonOptions.class) public abstract class LookupAndCallUnaryNode extends UnaryOpNode { - - public abstract static class NoAttributeHandler extends PNodeWithContext { - public abstract Object execute(Object receiver); - } - protected final TruffleString name; - protected final Supplier handlerFactory; - @Child private NoAttributeHandler handler; - public abstract Object executeObject(VirtualFrame frame, Object receiver); + public abstract Object executeObject(VirtualFrame frame, Object receiver) throws SpecialMethodNotFound; @Override public Object execute(VirtualFrame frame, Object receiver) { @@ -85,17 +81,11 @@ public Object execute(VirtualFrame frame, Object receiver) { @NeverDefault public static LookupAndCallUnaryNode create(TruffleString name) { - return LookupAndCallUnaryNodeGen.create(name, null); - } - - @NeverDefault - public static LookupAndCallUnaryNode create(TruffleString name, Supplier handlerFactory) { - return LookupAndCallUnaryNodeGen.create(name, handlerFactory); + return LookupAndCallUnaryNodeGen.create(name); } - LookupAndCallUnaryNode(TruffleString name, Supplier handlerFactory) { + LookupAndCallUnaryNode(TruffleString name) { this.name = name; - this.handlerFactory = handlerFactory; } public TruffleString getMethodName() { @@ -103,16 +93,25 @@ public TruffleString getMethodName() { } protected final PythonUnaryBuiltinNode getUnaryBuiltin(PythonBuiltinClassType clazz) { + CompilerAsserts.neverPartOfCompilation(); Object attribute = LookupAttributeInMRONode.Dynamic.getUncached().execute(clazz, name); if (attribute instanceof PBuiltinFunction) { PBuiltinFunction builtinFunction = (PBuiltinFunction) attribute; - if (PythonUnaryBuiltinNode.class.isAssignableFrom(builtinFunction.getBuiltinNodeFactory().getNodeClass())) { - return (PythonUnaryBuiltinNode) builtinFunction.getBuiltinNodeFactory().createNode(); + NodeFactory builtinNodeFactory = builtinFunction.getBuiltinNodeFactory(); + if (builtinNodeFactory != null && PythonUnaryBuiltinNode.class.isAssignableFrom(builtinNodeFactory.getNodeClass())) { + PythonUnaryBuiltinNode builtinNode = (PythonUnaryBuiltinNode) builtinNodeFactory.createNode(); + if (!callerExceedsMaxSize(builtinNode)) { + return builtinNode; + } } } return null; } + private boolean callerExceedsMaxSize(T builtinNode) { + return BuiltinInliningPolicy.exceedsCallerSize(BuiltinInliningPolicy.checkCallerSize(this, builtinNode)); + } + protected static PythonBuiltinClassType getBuiltinClass(Node inliningTarget, Object receiver, GetClassNode getClassNode) { Object clazz = getClassNode.execute(inliningTarget, receiver); return clazz instanceof PythonBuiltinClassType ? (PythonBuiltinClassType) clazz : null; @@ -124,7 +123,7 @@ protected static boolean isClazz(Node inliningTarget, PythonBuiltinClassType cla // Object - @Specialization(guards = {"clazz != null", "function != null", "isClazz(inliningTarget, clazz, receiver, getClassNode)"}, limit = "getCallSiteInlineCacheMaxDepth()") + @Specialization(guards = {"clazz != null", "function != null", "isClazz(inliningTarget, clazz, receiver, getClassNode)"}, limit = "1") static Object callObjectBuiltin(VirtualFrame frame, Object receiver, @SuppressWarnings("unused") @Bind Node inliningTarget, @SuppressWarnings("unused") @Shared @Cached GetClassNode getClassNode, @@ -133,45 +132,22 @@ static Object callObjectBuiltin(VirtualFrame frame, Object receiver, return function.execute(frame, receiver); } - @Specialization(guards = "getObjectClass(receiver) == cachedClass", limit = "3") - Object callObjectGeneric(VirtualFrame frame, Object receiver, - @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached("receiver.getClass()") Class cachedClass, - @Shared @Cached GetClassNode getClassNode, - @Shared @Cached("create(name)") LookupSpecialMethodNode getattr, - @Shared @Cached CallUnaryMethodNode dispatchNode) { - return doCallObject(frame, inliningTarget, receiver, getClassNode, getattr, dispatchNode); - } - - @Specialization(replaces = "callObjectGeneric") - @Megamorphic + @Specialization(replaces = "callObjectBuiltin") + @InliningCutoff @SuppressWarnings("truffle-static-method") - Object callObjectMegamorphic(VirtualFrame frame, Object receiver, + Object callObject(VirtualFrame frame, Object receiver, @Bind Node inliningTarget, - @Shared @Cached GetClassNode getClassNode, - @Shared @Cached("create(name)") LookupSpecialMethodNode getattr, - @Shared @Cached CallUnaryMethodNode dispatchNode) { - return doCallObject(frame, inliningTarget, receiver, getClassNode, getattr, dispatchNode); - } - - protected Class getObjectClass(Object object) { - return object.getClass(); - } - - private Object doCallObject(VirtualFrame frame, Node inliningTarget, Object receiver, GetClassNode getClassNode, LookupSpecialMethodNode getattr, CallUnaryMethodNode dispatchNode) { - Object attr = getattr.execute(frame, getClassNode.execute(inliningTarget, receiver), receiver); + @Cached InlinedBranchProfile notFoundProfile, + @Exclusive @Cached GetClassNode getClassNode, + @Cached("create(name)") LookupSpecialMethodNode getattr, + @Cached CallUnaryMethodNode dispatchNode) { + Object receiverClass = getClassNode.execute(inliningTarget, receiver); + Object attr = getattr.execute(frame, receiverClass, receiver); if (attr == PNone.NO_VALUE) { - if (handlerFactory != null) { - if (handler == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - handler = insert(handlerFactory.get()); - } - return handler.execute(receiver); - } - return PNone.NO_VALUE; - } else { - return dispatchNode.executeObject(frame, attr, receiver); + notFoundProfile.enter(inliningTarget); + throw SpecialMethodNotFound.INSTANCE; } + return dispatchNode.executeObject(frame, attr, receiver); } @GenerateUncached diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/AbstractObjectGetBasesNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/AbstractObjectGetBasesNode.java index 7370b57f4c..2a80ba9b32 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/AbstractObjectGetBasesNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/AbstractObjectGetBasesNode.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,7 +44,9 @@ import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyObjectLookupAttr; +import com.oracle.graal.python.lib.PyTupleCheckNode; import com.oracle.graal.python.nodes.PNodeWithContext; +import com.oracle.graal.python.nodes.builtins.TupleNodes.ConstructTupleNode; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; @@ -67,10 +69,14 @@ public static AbstractObjectGetBasesNode getUncached() { @Specialization static PTuple getBasesCached(VirtualFrame frame, Node inliningTarget, Object cls, - @Cached PyObjectLookupAttr lookupAttr) { + @Cached PyObjectLookupAttr lookupAttr, + @Cached PyTupleCheckNode tupleCheck, + @Cached ConstructTupleNode constructTupleNode) { Object bases = lookupAttr.execute(frame, inliningTarget, cls, T___BASES__); if (bases instanceof PTuple) { return (PTuple) bases; + } else if (tupleCheck.execute(inliningTarget, bases)) { + return constructTupleNode.execute(frame, bases); } return null; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/IsSubtypeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/IsSubtypeNode.java index 0232db2025..563e9a35fd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/IsSubtypeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/classes/IsSubtypeNode.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 @@ -165,6 +165,10 @@ protected static int sub(int a, int b) { return a - b; } + protected static MroSequenceStorage getMroUncached(Object cls) { + return GetMroStorageNode.executeUncached(cls); + } + @Specialization(guards = { "cachedCls != null", "getType(inliningTarget, cls, builtinTypeProfile, builtinClassProfile) == cachedCls", @@ -200,13 +204,12 @@ static boolean isVariableSubtypeOfConstantTypeCachedMultiContext(@SuppressWarnin @SuppressWarnings("unused") static boolean isSubtypeOfCached(Object derived, Object cls, @Bind Node inliningTarget, - @Cached("derived") Object cachedDerived, - @Cached("cls") Object cachedCls, + @Cached(value = "derived", weak = true) Object cachedDerived, + @Cached(value = "cls", weak = true) Object cachedCls, @Shared @Cached IsSameTypeNode isSameDerivedNode, @Shared @Cached IsSameTypeNode isSameClsNode, @Shared @Cached IsSameTypeNode isSameTypeInLoopNode, - @Shared @Cached GetMroStorageNode getMro, - @Cached("getMro.execute(inliningTarget, cachedDerived)") MroSequenceStorage mro, + @Cached(value = "getMroUncached(derived)", weak = true) MroSequenceStorage mro, @Cached("isInMro(inliningTarget, cachedCls, mro, mro.getInternalClassArray().length, isSameTypeInLoopNode)") boolean isInMro) { return isInMro; } @@ -225,9 +228,8 @@ static boolean isSubtypeOfCached(Object derived, Object cls, @InliningCutoff static boolean isSubtypeOfVariableTypeCached(@SuppressWarnings("unused") Object derived, Object cls, @Bind Node inliningTarget, - @Cached("derived") @SuppressWarnings("unused") Object cachedDerived, - @SuppressWarnings("unused") @Shared @Cached GetMroStorageNode getMro, - @Cached("getMro.execute(inliningTarget, cachedDerived)") MroSequenceStorage mro, + @Cached(value = "derived", weak = true) @SuppressWarnings("unused") Object cachedDerived, + @Cached(value = "getMroUncached(derived)", weak = true) MroSequenceStorage mro, @Cached("mro.getInternalClassArray().length") int sz, @Shared @Cached IsSameTypeNode isSameTypeInLoopNode, @Shared @Cached @SuppressWarnings("unused") IsSameTypeNode isSameDerivedNode) { @@ -252,9 +254,9 @@ static boolean isSubtypeOfVariableTypeCached(@SuppressWarnings("unused") Object @InliningCutoff static boolean isVariableSubtypeOfConstantTypeCached(@SuppressWarnings("unused") Object derived, @SuppressWarnings("unused") Object cls, @Bind Node inliningTarget, - @Cached("cls") @SuppressWarnings("unused") Object cachedCls, + @Cached(value = "cls", weak = true) @SuppressWarnings("unused") Object cachedCls, @SuppressWarnings("unused") @Shared @Cached GetMroStorageNode getMro, - @SuppressWarnings("unused") @Cached("getMro.execute(inliningTarget, cachedCls)") MroSequenceStorage baseMro, + @SuppressWarnings("unused") @Cached(value = "getMroUncached(cls)", weak = true) MroSequenceStorage baseMro, @Shared @Cached IsSameTypeNode isSameTypeInLoopNode, @Bind("getMro.execute(inliningTarget, derived).getInternalClassArray()") PythonAbstractClass[] mroAry, @SuppressWarnings("unused") @Cached("mroAry.length") int derivedMroLen, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/EncapsulateExceptionGroupNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/EncapsulateExceptionGroupNode.java index c658092f73..8babbe6225 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/EncapsulateExceptionGroupNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/EncapsulateExceptionGroupNode.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 @@ -42,6 +42,7 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.exception.BaseExceptionGroupBuiltins; import com.oracle.graal.python.builtins.objects.exception.PBaseException; import com.oracle.graal.python.builtins.objects.exception.PBaseExceptionGroup; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; @@ -102,9 +103,7 @@ public static Object matchExceptions(VirtualFrame frame, if (isBaseExceptionGroupProfile.profile(inliningTarget, exceptionObj instanceof PBaseExceptionGroup)) { PBaseExceptionGroup group = (PBaseExceptionGroup) exceptionObj; if (groupContainsReraisesProfile.profile(inliningTarget, group.getContainsReraises())) { - for (Object e : group.getExceptions()) { - reraisedUnhandledExceptions.add(e); - } + reraisedUnhandledExceptions.add(group); } else { exceptionGroupList.add(group); } @@ -117,12 +116,8 @@ public static Object matchExceptions(VirtualFrame frame, return PException.fromExceptionInfo(exceptionGroup, PythonOptions.isPExceptionWithJavaStacktrace(language)); } if (reraisedOrUnhandledSizeProfile.profile(inliningTarget, reraisedUnhandledExceptions.size() != 0)) { - PBaseExceptionGroup reraisedGroup = (PBaseExceptionGroup) deriveExceptionGroup.execute( - frame, - inliningTarget, - exceptionOrigUnreified, - T_DERIVE, - PFactory.createTuple(language, reraisedUnhandledExceptions.toArray(new Object[0]))); + PBaseExceptionGroup reraisedGroup = BaseExceptionGroupBuiltins.exceptionGroupProjection( + inliningTarget, (PBaseExceptionGroup) exceptionOrigUnreified, reraisedUnhandledExceptions.toArray(new Object[0])); reraisedGroup.setParent(exceptionGroup.getParent()); exceptionGroupList.add(reraisedGroup); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java index 372cde5aca..bcfbaa552b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/ExceptMatchNode.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,10 +42,11 @@ import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.exception.PBaseException; -import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.lib.PyTupleCheckNode; 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.builtins.TupleNodes; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.exception.PException; @@ -55,6 +56,7 @@ import com.oracle.truffle.api.bytecode.OperationProxy; 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.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; @@ -69,7 +71,7 @@ @ImportStatic(PGuards.class) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = true) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = true) @GenerateInline(false) // footprint reduction 44 -> 25 public abstract class ExceptMatchNode extends Node { public abstract boolean executeMatch(Object exception, Object clause); @@ -80,9 +82,10 @@ private static void raiseIfNoException(Node inliningTarget, Object clause, Valid } } - @Specialization(guards = "!isPTuple(clause)") + @Specialization(guards = "!tupleCheck.execute(inliningTarget, clause)") public static boolean matchPythonSingle(PException e, Object clause, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheck, @Shared @Cached ValidExceptionNode isValidException, @Shared @Cached GetClassNode getClassNode, @Shared @Cached IsSubtypeNode isSubtype) { @@ -90,9 +93,10 @@ public static boolean matchPythonSingle(PException e, Object clause, return isSubtype.execute(getClassNode.execute(inliningTarget, e.getUnreifiedException()), clause); } - @Specialization(guards = "!isPTuple(clause)") + @Specialization(guards = "!tupleCheck.execute(inliningTarget, clause)") public static boolean matchPythonBaseSingle(PBaseException e, Object clause, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheck, @Shared @Cached ValidExceptionNode isValidException, @Shared @Cached GetClassNode getClassNode, @Shared @Cached IsSubtypeNode isSubtype) { @@ -100,9 +104,10 @@ public static boolean matchPythonBaseSingle(PBaseException e, Object clause, return isSubtype.execute(getClassNode.execute(inliningTarget, e), clause); } - @Specialization(guards = {"!isPTuple(clause)", "!isPException(e)"}, limit = "1") + @Specialization(guards = {"!tupleCheck.execute(inliningTarget, clause)", "!isPException(e)"}, limit = "1") public static boolean matchJava(AbstractTruffleException e, Object clause, @Bind Node inliningTarget, + @SuppressWarnings("unused") @Shared("tupleCheck") @Cached PyTupleCheckNode tupleCheck, @Shared @Cached ValidExceptionNode isValidException, @CachedLibrary("clause") InteropLibrary clauseLib) { // n.b.: we can only allow Java exceptions in clauses, because we cannot tell for other @@ -120,13 +125,15 @@ public static boolean matchJava(AbstractTruffleException e, Object clause, } } - @Specialization - public static boolean matchTuple(Object e, PTuple clause, + @Specialization(guards = "tupleCheck.execute(inliningTarget, clause)", limit = "1") + public static boolean matchTuple(Object e, Object clause, @Bind Node inliningTarget, - @Cached ExceptMatchNode recursiveNode, - @Cached SequenceStorageNodes.GetItemScalarNode getItemNode) { + @SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck, + @Exclusive @Cached ExceptMatchNode recursiveNode, + @Exclusive @Cached TupleNodes.GetTupleStorage getTupleStorage, + @Exclusive @Cached SequenceStorageNodes.GetItemScalarNode getItemNode) { // check for every type in the tuple - SequenceStorage storage = clause.getSequenceStorage(); + SequenceStorage storage = getTupleStorage.execute(inliningTarget, clause); int length = storage.length(); for (int i = 0; i < length; i++) { Object clauseType = getItemNode.execute(inliningTarget, storage, i); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java index 216a0c2602..f0f89e879c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java @@ -42,16 +42,20 @@ import static com.oracle.graal.python.builtins.modules.io.IONodes.T_WRITE; import static com.oracle.graal.python.nodes.BuiltinNames.T_SYS; +import static com.oracle.graal.python.nodes.BuiltinNames.T___BUILTINS__; import static com.oracle.graal.python.runtime.exception.PythonErrorType.SystemExit; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; 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.code.PCode; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; import com.oracle.graal.python.builtins.objects.exception.PBaseException; import com.oracle.graal.python.builtins.objects.function.PArguments; +import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; @@ -67,6 +71,7 @@ import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; +import com.oracle.graal.python.runtime.PythonSourceOptions; import com.oracle.graal.python.runtime.exception.ExceptionUtils; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.exception.PythonExitException; @@ -86,12 +91,14 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.strings.TruffleString; public final class TopLevelExceptionHandler extends RootNode { private final RootCallTarget innerCallTarget; private final PException exception; private final SourceSection sourceSection; private final Source source; + private final boolean newGlobals; @Child private GilNode gilNode = GilNode.create(); @@ -113,6 +120,7 @@ public TopLevelExceptionHandler(PythonLanguage language, RootNode child, Source if (child instanceof PBytecodeRootNode) { instrumentationForwarder = ((PBytecodeRootNode) child).createInstrumentationMaterializationForwarder(); } + this.newGlobals = source.getOptions(language).get(PythonSourceOptions.NewGlobals); } public TopLevelExceptionHandler(PythonLanguage language, PException exception) { @@ -121,6 +129,7 @@ public TopLevelExceptionHandler(PythonLanguage language, PException exception) { this.innerCallTarget = null; this.exception = exception; this.source = null; + this.newGlobals = false; } private PythonLanguage getPythonLanguage() { @@ -213,6 +222,9 @@ private AbstractTruffleException handlePythonException(AbstractTruffleException } private void exit(int exitCode) { + if (getSourceSection() == null) { + throw new PythonExitException(this, exitCode); + } if (!getSourceSection().getSource().isInteractive()) { if (getContext().isChildContext()) { getContext().getChildContextData().setExitCode(1); @@ -255,7 +267,16 @@ public SourceSection getSourceSection() { @TruffleBoundary private void handleSystemExit(PBaseException pythonException) { PythonContext theContext = getContext(); - if (theContext.getOption(PythonOptions.InspectFlag) && !getSourceSection().getSource().isInteractive()) { + + boolean executingNonInteractiveSource; + if (source != null) { + executingNonInteractiveSource = !source.isInteractive(); + } else { + SourceSection section = getSourceSection(); + executingNonInteractiveSource = section != null && !section.getSource().isInteractive(); + } + + if (theContext.getOption(PythonOptions.InspectFlag) && executingNonInteractiveSource) { // Don't exit if -i flag was given and we're not yet running interactively return; } @@ -317,14 +338,21 @@ private Object run(VirtualFrame frame) { PythonContext pythonContext = getContext(); PythonModule mainModule = null; PythonLanguage language = getPythonLanguage(); + PCode code = PFactory.createCode(language, innerCallTarget, PythonUtils.internString(TruffleString.fromJavaStringUncached(source.getName(), TS_ENCODING))); + PArguments.setCodeObject(arguments, code); if (source.isInternal()) { // internal sources are not run in the main module PArguments.setGlobals(arguments, PFactory.createDict(language)); } else { - mainModule = pythonContext.getMainModule(); - PDict mainDict = GetOrCreateDictNode.executeUncached(mainModule); - PArguments.setGlobals(arguments, mainDict); - PArguments.setSpecialArgument(arguments, mainDict); + PDict globals; + if (newGlobals) { + globals = PFactory.createDict(language, new PKeyword[]{new PKeyword(T___BUILTINS__, pythonContext.getBuiltins())}); + } else { + mainModule = pythonContext.getMainModule(); + globals = GetOrCreateDictNode.executeUncached(mainModule); + } + PArguments.setGlobals(arguments, globals); + PArguments.setSpecialArgument(arguments, globals); PArguments.setException(arguments, PException.NO_EXCEPTION); } // At the top level we don't have a real Python frame at hand, so we go through 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/MaterializeFrameNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/MaterializeFrameNode.java index 97b85c95a1..a48124ca1d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/MaterializeFrameNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/MaterializeFrameNode.java @@ -45,6 +45,7 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.objects.frame.PFrame; import com.oracle.graal.python.builtins.objects.function.PArguments; +import com.oracle.graal.python.builtins.objects.function.PFunction; import com.oracle.graal.python.builtins.objects.generator.PGenerator; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.nodes.PRootNode; @@ -72,6 +73,7 @@ import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.MaterializedFrame; +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.InlinedIntValueProfile; @@ -138,9 +140,13 @@ public final PFrame executeOnStack(boolean markAsEscaped, boolean forceSync, Fra location = PArguments.getCurrentFrameInfo(frameToMaterialize).getRootNode(); } } else { - // We will need EncapsulatingNodeReference or thread the BytecodeNode as argument for - // BytecodeDSL uncached execution - assert this.isAdoptable(); + if (!this.isAdoptable()) { + // This can happen in the uncached interpreter, but there the UncachedBytecodeNode + // should set itself as encapsulating node before it starts executing its bytecode + location = EncapsulatingNodeReference.getCurrent().get(); + assert location != null; + assert BytecodeNode.get(location) != null; + } } return execute(location, markAsEscaped, forceSync, frameToMaterialize); } @@ -168,7 +174,7 @@ public final PFrame execute(Node location, boolean markAsEscaped, boolean forceS static PFrame freshPFrameCachedFD(Node location, boolean markAsEscaped, boolean forceSync, Frame frameToMaterialize, @Bind PythonLanguage language, @Shared("syncValuesNode") @Cached SyncFrameValuesNode syncValuesNode) { - PFrame escapedFrame = PFactory.createPFrame(language, PArguments.getCurrentFrameInfo(frameToMaterialize), location, false); + PFrame escapedFrame = PFactory.createPFrame(language, PArguments.getCurrentFrameInfo(frameToMaterialize), location, PArguments.getFunctionOrCodeObject(frameToMaterialize), false); return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, forceSync, location, syncValuesNode); } @@ -176,7 +182,7 @@ static PFrame freshPFrameCachedFD(Node location, boolean markAsEscaped, boolean static PFrame freshPFrameCustomLocals(Node location, boolean markAsEscaped, @SuppressWarnings("unused") boolean forceSync, Frame frameToMaterialize, @Bind PythonLanguage language) { - PFrame escapedFrame = PFactory.createPFrame(language, PArguments.getCurrentFrameInfo(frameToMaterialize), location, true); + PFrame escapedFrame = PFactory.createPFrame(language, PArguments.getCurrentFrameInfo(frameToMaterialize), location, PArguments.getFunctionOrCodeObject(frameToMaterialize), true); escapedFrame.setLocalsDict(PArguments.getSpecialArgument(frameToMaterialize)); return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, false, location, null); } @@ -185,7 +191,7 @@ static PFrame freshPFrameCustomLocals(Node location, boolean markAsEscaped, @Sup static PFrame freshPFrameForGenerator(Node location, @SuppressWarnings("unused") boolean markAsEscaped, @SuppressWarnings("unused") boolean forceSync, Frame frameToMaterialize) { MaterializedFrame generatorFrame = PGenerator.getGeneratorFrame(frameToMaterialize); PFrame.Reference frameRef = PArguments.getCurrentFrameInfo(frameToMaterialize); - PFrame escapedFrame = materializeGeneratorFrame(location, generatorFrame, PArguments.getGlobals(frameToMaterialize), frameRef); + PFrame escapedFrame = materializeGeneratorFrame(location, generatorFrame, PArguments.getFunctionObject(frameToMaterialize), PArguments.getGlobals(frameToMaterialize), frameRef); return doEscapeFrame(frameToMaterialize, escapedFrame, markAsEscaped, false, location, null); } @@ -204,12 +210,13 @@ static PFrame alreadyEscapedFrame(@SuppressWarnings("unused") Node location, boo return pyFrame; } - public static PFrame materializeGeneratorFrame(Node location, MaterializedFrame generatorFrame, PythonObject globals, PFrame.Reference frameRef) { - return materializeGeneratorFrame(PythonLanguage.get(location), location, generatorFrame, globals, frameRef); + public static PFrame materializeGeneratorFrame(Node location, MaterializedFrame generatorFrame, PFunction generatorFunction, PythonObject globals, PFrame.Reference frameRef) { + return materializeGeneratorFrame(PythonLanguage.get(location), location, generatorFrame, generatorFunction, globals, frameRef); } - public static PFrame materializeGeneratorFrame(PythonLanguage language, Node location, MaterializedFrame generatorFrame, PythonObject globals, PFrame.Reference frameRef) { - PFrame escapedFrame = PFactory.createPFrame(language, frameRef, location, false); + public static PFrame materializeGeneratorFrame(PythonLanguage language, Node location, MaterializedFrame generatorFrame, PFunction generatorFunction, PythonObject globals, + PFrame.Reference frameRef) { + PFrame escapedFrame = PFactory.createPFrame(language, frameRef, location, generatorFunction, false); if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { BytecodeNode bytecodeNode = BytecodeNode.get(location); assert bytecodeNode != null : location; 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 331a22c02f..24840959fc 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 @@ -52,6 +52,7 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; 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.PArguments; import com.oracle.graal.python.builtins.objects.generator.PGenerator; import com.oracle.graal.python.nodes.PRaiseNode; @@ -60,6 +61,7 @@ import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.runtime.CallerFlags; +import com.oracle.graal.python.runtime.ExecutionContext.CalleeContext; import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.IndirectCallData; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; @@ -69,6 +71,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.ThreadLocalAction; +import com.oracle.truffle.api.ThreadLocalAction.Access; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleSafepoint; import com.oracle.truffle.api.bytecode.ContinuationRootNode; @@ -80,6 +83,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; import com.oracle.truffle.api.frame.FrameInstanceVisitor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; @@ -139,6 +143,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) @@ -167,13 +175,17 @@ public final PFrame getFrameForReference(Frame frame, PFrame.Reference startFram } public final PFrame getFrameForReference(Frame frame, PFrame.Reference startFrameInfo, FrameSelector selector, int level, int callerFlags) { - return execute(frame, startFrameInfo, FrameInstance.FrameAccess.READ_ONLY, selector, level, callerFlags | CallerFlags.NEEDS_PFRAME); + return getFrameForReference(frame, startFrameInfo, selector, level, callerFlags, null); + } + + public final PFrame getFrameForReference(Frame frame, PFrame.Reference startFrameInfo, FrameSelector selector, int level, int callerFlags, Thread frameThread) { + return execute(frame, startFrameInfo, FrameInstance.FrameAccess.READ_ONLY, selector, level, callerFlags | CallerFlags.NEEDS_PFRAME, frameThread); } - protected abstract PFrame execute(Frame frame, PFrame.Reference startFrameInfo, FrameInstance.FrameAccess frameAccess, FrameSelector selector, int level, int callerFlags); + protected abstract PFrame execute(Frame frame, PFrame.Reference startFrameInfo, FrameInstance.FrameAccess frameAccess, FrameSelector selector, int level, int callerFlags, Thread frameThread); @Specialization - PFrame read(VirtualFrame frame, PFrame.Reference startFrameInfo, FrameInstance.FrameAccess frameAccess, FrameSelector selector, int level, int callerFlags, + PFrame read(VirtualFrame frame, PFrame.Reference startFrameInfo, FrameInstance.FrameAccess frameAccess, FrameSelector selector, int level, int callerFlags, Thread frameThread, @Bind Node inliningTarget, @Cached MaterializeFrameNode materializeFrameNode, @Cached InlinedBranchProfile stackWalkProfile1, @@ -229,30 +241,24 @@ PFrame read(VirtualFrame frame, PFrame.Reference startFrameInfo, FrameInstance.F * It is necessary to continue from where we stopped with the backref walk because the * original starting frame might not be on stack anymore */ - return readSlowPath(curFrameInfo, frameAccess, selector, level - i, callerFlags, materializeFrameNode); + return readSlowPath(curFrameInfo, frameAccess, selector, level - i, callerFlags, frameThread, materializeFrameNode); } @TruffleBoundary @SuppressWarnings("try") - private PFrame readSlowPath(PFrame.Reference startFrameInfo, FrameInstance.FrameAccess frameAccess, FrameSelector selector, int level, int callerFlags, + private PFrame readSlowPath(PFrame.Reference startFrameInfo, FrameInstance.FrameAccess frameAccess, FrameSelector selector, int level, int callerFlags, Thread frameThread, MaterializeFrameNode materializeFrameNode) { - if (level == 0 && startFrameInfo != null && startFrameInfo.getPyFrame() != null && !selector.skip(startFrameInfo.getRootNode()) && startFrameInfo.getPyFrame().getThread() != null && - startFrameInfo.getPyFrame().getThread() != Thread.currentThread()) { - // We have the frame we're looking for, but it's on another thread - Thread thread = startFrameInfo.getPyFrame().getThread(); + Thread thread = getFrameThread(startFrameInfo, frameThread); + if (startFrameInfo != null && thread != null && thread != Thread.currentThread()) { + // We have a frame reference on another thread. Resolve the requested level there. + PFrame[] pyFrameResult = new PFrame[1]; if (thread.isAlive()) { try (var gil = GilNode.uncachedRelease()) { // Schedule a safepoint action on that thread Future future = PythonContext.get(null).getEnv().submitThreadLocal(new Thread[]{thread}, new ThreadLocalAction(true, false) { @Override protected void perform(Access access) { - Node location = access.getLocation(); - if (location instanceof PBytecodeDSLRootNode) { - // See AsyncPythonAction#execute for explanation - location = PythonLanguage.get(null).unavailableSafepointLocation; - } - StackWalkResult result = ReadFrameNode.getFrame(location, startFrameInfo, frameAccess, selector, 0, callerFlags); - processStackWalkResult(materializeFrameNode, callerFlags, result); + pyFrameResult[0] = readFrameInThreadLocal(access, startFrameInfo, frameAccess, selector, level, callerFlags, materializeFrameNode); } }); TruffleSafepoint.setBlockedThreadInterruptible(this, voidFuture -> { @@ -268,28 +274,67 @@ protected void perform(Access access) { }, future); } } - assert !startFrameInfo.getPyFrame().outdatedCallerFlags(callerFlags); - return startFrameInfo.getPyFrame(); + if (pyFrameResult[0] != null) { + return pyFrameResult[0]; + } + if (level == 0 && !selector.skip(startFrameInfo.getRootNode())) { + PFrame pyFrame = startFrameInfo.getPyFrame(); + if (pyFrame != null) { + assert !pyFrame.outdatedCallerFlags(callerFlags); + return pyFrame; + } + } } StackWalkResult callerFrameResult = getFrame(this, startFrameInfo, frameAccess, selector, level, callerFlags); return processStackWalkResult(materializeFrameNode, callerFlags, callerFrameResult); } + public static PFrame readFrameInThreadLocal(Access access, Reference startFrameInfo, FrameAccess frameAccess, FrameSelector selector, int level, int callerFlags, + MaterializeFrameNode materializeFrameNode) { + return readFrameInThreadLocal(access, startFrameInfo, frameAccess, selector, level, callerFlags, materializeFrameNode, false); + } + + public static PFrame readFrameInThreadLocal(Access access, Reference startFrameInfo, FrameAccess frameAccess, FrameSelector selector, int level, int callerFlags, + MaterializeFrameNode materializeFrameNode, boolean forceEscape) { + Node location = access.getLocation(); + if (location instanceof PBytecodeDSLRootNode) { + // See AsyncPythonAction#execute for explanation + location = PythonLanguage.get(null).unavailableSafepointLocation; + } + StackWalkResult result = ReadFrameNode.getFrame(location, startFrameInfo, frameAccess, selector, level, callerFlags); + if (forceEscape && result != null) { + CalleeContext.forceEscapeFrame(result.frame, getMaterializationLocation(result), PArguments.getCurrentFrameInfo(result.frame), materializeFrameNode); + } + return processStackWalkResult(materializeFrameNode, callerFlags, result); + } + + private static Thread getFrameThread(PFrame.Reference startFrameInfo, Thread frameThread) { + if (startFrameInfo != null && startFrameInfo.getPyFrame() != null) { + return startFrameInfo.getPyFrame().getThread(); + } + return frameThread; + } + private static PFrame processStackWalkResult(MaterializeFrameNode materializeFrameNode, int callerFlags, StackWalkResult callerFrameResult) { if (callerFrameResult != null) { - Node location = callerFrameResult.callNode; - if (!(callerFrameResult.rootNode instanceof PBytecodeDSLRootNode) && location == null) { - /* - * We can fixup the location like this only for other root nodes, for Bytecode DSL - * we need the BytecodeNode - */ - location = callerFrameResult.rootNode; - } + Node location = getMaterializationLocation(callerFrameResult); return materializeFrameNode.execute(location, false, CallerFlags.needsLocals(callerFlags), callerFrameResult.frame); } return null; } + private static Node getMaterializationLocation(StackWalkResult callerFrameResult) { + Node location = callerFrameResult.callNode; + if (!(callerFrameResult.rootNode instanceof PBytecodeDSLRootNode) && location == null) { + /* + * We can fixup the location like this only for other root nodes, for Bytecode DSL we + * need the BytecodeNode + */ + location = callerFrameResult.rootNode; + } + return location; + } + /** * Walk up the stack to find the currently top Python frame. This method is mostly useful for * code that cannot accept a {@code VirtualFrame} parameter (e.g. library code). It is necessary @@ -440,7 +485,13 @@ public StackWalkResult visitFrame(FrameInstance frameInstance) { if (!selector.skip(pRootNode)) { if (i == level) { Frame frame = ReadFrameNode.getFrame(frameInstance, frameAccess); - assert PArguments.isPythonFrame(frame); + /* + * It's possible that an async action interrupts a frame before the + * callee context initialized the frame reference, skip it then + */ + if (PArguments.getCurrentFrameInfo(frame) == null) { + return null; + } IndirectCallData.setCallerFlagsOnIndirectCallData(callNode, callerFlags); if (prevRootNode instanceof PRootNode prevPRootNode && prevPRootNode.setsUpCalleeContext()) { // Update the flags in the callee diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java index 56bb66ba48..3234ff7a8d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java @@ -63,7 +63,7 @@ @GenerateUncached @GenerateInline(false) // footprint reduction 48 -> 30 -@Proxyable(storeBytecodeIndex = false) +@Proxyable(storeBytecodeIndex = false, allowUncached = true) @ConstantOperand(type = TruffleString.class) @ImportStatic(PGuards.class) public abstract class ReadGlobalOrBuiltinNode extends Node { @@ -126,7 +126,7 @@ public static Object readFastFromGlobalStore(VirtualFrame frame, TruffleString n @ForceQuickening @Specialization(guards = "!isNoValue(result)", replaces = "readBuiltinFastPath", excludeForUncached = true, limit = "1") public static Object readGlobalFastPath(VirtualFrame frame, TruffleString attributeId, - @Cached ReadAttributeFromPythonObjectNode readNode, + @Cached(inline = false) ReadAttributeFromPythonObjectNode readNode, @Bind("readFastFromGlobalStore(frame, attributeId, readNode)") Object result) { return result; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/interop/GetInteropBehaviorValueNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/interop/GetInteropBehaviorValueNode.java index efa8715cf0..38247c171f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/interop/GetInteropBehaviorValueNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/interop/GetInteropBehaviorValueNode.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,8 +46,10 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.PythonAbstractObject; +import com.oracle.graal.python.builtins.objects.function.PFunction; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.call.CallDispatchers; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToJavaBooleanNode; @@ -60,7 +62,6 @@ import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.util.PythonUtils; -import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateInline; @@ -68,8 +69,6 @@ import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.nodes.DirectCallNode; -import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; @GenerateUncached @@ -186,16 +185,16 @@ static Object getValueConstantBoolean(@SuppressWarnings("unused") Node inliningT @Specialization(guards = {"!behavior.isConstant(method)", "method.checkArity(extraArguments)"}) static Object getValue(Node inliningTarget, InteropBehavior behavior, InteropBehaviorMethod method, PythonAbstractObject receiver, Object[] extraArguments, - @Cached SimpleInvokeNodeDispatch invokeNode, + @Cached CallDispatchers.FunctionCachedInvokeNode invokeNode, @Cached ConvertJavaStringArguments convertArgs, @Cached IsBuiltinObjectProfile unsupportedMessageProfile, @Cached GilNode gil) throws UnsupportedMessageException { assert behavior.isDefined(method) : "interop behavior method is not defined!"; - CallTarget callTarget = behavior.getCallTarget(method); + PFunction function = behavior.getFunction(method); Object[] pArguments = behavior.createArguments(method, receiver, convertArgs.execute(inliningTarget, extraArguments)); boolean mustRelease = gil.acquire(); try { - return invokeNode.execute(inliningTarget, callTarget, pArguments); + return invokeNode.execute(null, inliningTarget, function, pArguments); } catch (PException pe) { pe.expect(inliningTarget, PythonBuiltinClassType.UnsupportedMessage, unsupportedMessageProfile); throw UnsupportedMessageException.create(); @@ -221,25 +220,6 @@ public static GetInteropBehaviorValueNode getUncached() { return GetInteropBehaviorValueNodeGen.getUncached(); } - @GenerateUncached - @GenerateInline - abstract static class SimpleInvokeNodeDispatch extends Node { - public abstract Object execute(Node inliningTarget, CallTarget callTarget, Object[] arguments); - - @Specialization(guards = {"cachedCallTarget == callTarget"}, limit = "3") - static Object doDirectCall(CallTarget callTarget, Object[] arguments, - @Cached("callTarget") CallTarget cachedCallTarget, - @Cached("create(callTarget)") DirectCallNode directCallNode) { - return directCallNode.call(arguments); - } - - @Specialization(replaces = "doDirectCall") - static Object doIndirectCall(CallTarget callTarget, Object[] arguments, - @Cached IndirectCallNode indirectCallNode) { - return indirectCallNode.call(callTarget, arguments); - } - } - @GenerateUncached @GenerateInline abstract static class ConvertJavaStringArguments extends Node { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/interop/InteropBehavior.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/interop/InteropBehavior.java index 6bf73f17b5..a23513646a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/interop/InteropBehavior.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/interop/InteropBehavior.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,9 +47,7 @@ import com.oracle.graal.python.builtins.objects.PythonAbstractObject; import com.oracle.graal.python.builtins.objects.function.PArguments; import com.oracle.graal.python.builtins.objects.function.PFunction; -import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.util.PythonUtils; -import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.TruffleLogger; @@ -60,7 +58,7 @@ public class InteropBehavior { private static final TruffleLogger LOGGER = PythonLanguage.getLogger(LOGGER_INTEROP_BEHAVIOR_NAME); @ValueType - private record InteropBehaviorMethodRecord(CallTarget callTarget, PythonObject globals, boolean constant) { + private record InteropBehaviorMethodRecord(PFunction function, boolean constant) { } private final PythonAbstractObject receiver; @@ -72,14 +70,14 @@ public InteropBehavior(PythonAbstractObject receiver) { } public void defineBehavior(InteropBehaviorMethod method, PFunction function) { - records[method.ordinal()] = new InteropBehaviorMethodRecord(function.getCode().getRootCallTarget(), function.getGlobals(), false); + records[method.ordinal()] = new InteropBehaviorMethodRecord(function, false); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine(PythonUtils.formatJString("register %s.%s interop extension as function", receiver, method.name)); } } public void defineBehavior(InteropBehaviorMethod method, boolean constant) { - records[method.ordinal()] = new InteropBehaviorMethodRecord(null, null, constant); + records[method.ordinal()] = new InteropBehaviorMethodRecord(null, constant); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine(PythonUtils.formatJString("register %s.%s interop extension as constant (%s)", receiver, method.name, constant)); } @@ -89,19 +87,14 @@ public boolean isDefined(InteropBehaviorMethod method) { return records[method.ordinal()] != null; } - public CallTarget getCallTarget(InteropBehaviorMethod method) { + public PFunction getFunction(InteropBehaviorMethod method) { assert isDefined(method) : "interop behavior method not defined"; - return records[method.ordinal()].callTarget; - } - - public PythonObject getGlobals(InteropBehaviorMethod method) { - assert isDefined(method) : "interop behavior method not defined"; - return records[method.ordinal()].globals; + return records[method.ordinal()].function; } public boolean isConstant(InteropBehaviorMethod method) { assert isDefined(method) : "interop behavior method not defined"; - return records[method.ordinal()].callTarget == null; + return records[method.ordinal()].function == null; } public boolean getConstantValue(InteropBehaviorMethod method) { @@ -112,7 +105,6 @@ public boolean getConstantValue(InteropBehaviorMethod method) { public Object[] createArguments(InteropBehaviorMethod method, PythonAbstractObject receiver, Object[] extraArguments) { assert method.checkArity(extraArguments); Object[] pArguments = PArguments.create(1 + (method.takesVarArgs ? 1 : method.extraArguments)); - PArguments.setGlobals(pArguments, getGlobals(method)); PArguments.setArgument(pArguments, 0, receiver); if (method.takesVarArgs) { PArguments.setArgument(pArguments, 1, extraArguments); 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/nodes/object/DescriptorCheckNode.java similarity index 61% 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/nodes/object/DescriptorCheckNode.java index 93c82d49e6..3562ce1319 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/nodes/object/DescriptorCheckNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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 @@ -38,45 +38,35 @@ * 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.nodes.object; -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 static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; + +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.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.InteropLibrary; import com.oracle.truffle.api.nodes.Node; -/** - * 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. - */ -@GenerateUncached @GenerateInline @GenerateCached(false) -public abstract class GetReplacementNode extends Node { - - public abstract Object execute(Node inliningTarget, PythonNativeWrapper wrapper); - - @Specialization - static Object doReplacingWrapper(PyMemoryViewWrapper wrapper) { - return wrapper.getReplacement(); - } +@GenerateUncached +public abstract class DescriptorCheckNode extends Node { + public abstract void execute(Node inliningTarget, Object descrType, Object nameObj, Object obj); + // https://github.com/python/cpython/blob/e8b19656396381407ad91473af5da8b0d4346e88/Objects/descrobject.c#L70 @Specialization - static Object doReplacingWrapper(PythonClassNativeWrapper wrapper) { - return wrapper.getReplacement(); - } - - @Fallback - static Object doWrapper(Node inliningTarget, PythonNativeWrapper wrapper) { - return null; + static void check(Node inliningTarget, Object descrType, Object name, Object obj, + @Cached GetClassNode getClassNode, + @Cached(inline = false) IsSubtypeNode isSubtypeNode, + @Cached PRaiseNode raiseNode) { + Object type = getClassNode.execute(inliningTarget, obj); + if (!isSubtypeNode.execute(type, descrType)) { + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.DESC_S_FOR_N_DOESNT_APPLY_TO_N, name, descrType, type); + } } } 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 f4c91a272c..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 @@ -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 @@ -46,10 +46,10 @@ 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; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Cached; @@ -147,12 +147,14 @@ public static Object executeUncached(PythonObject object) { public abstract Object execute(Node inliningTarget, PythonAbstractNativeObject object); + // The dynamicType field is final, so the DSL shouldn't generate a runtime guard since we + // cache on the shape @Idempotent - static Object getDynamicType(Shape shape) { - return shape.getDynamicType(); + static boolean dynamicTypeIsPythonClass(Shape shape) { + return PGuards.isPythonClass(shape.getDynamicType()); } - @Specialization(guards = {"object.getShape() == cachedShape", "isPythonClass(getDynamicType(cachedShape))"}, limit = "1") + @Specialization(guards = {"object.getShape() == cachedShape", "dynamicTypeIsPythonClass(cachedShape)"}, limit = "1") static Object doConstantClass(@SuppressWarnings("unused") PythonObject object, @Cached(value = "object.getShape()") Shape cachedShape) { return cachedShape.getDynamicType(); @@ -191,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 fc77df4d78..d3d0bfaf34 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; @@ -103,7 +110,7 @@ static PDict getNoDict(@SuppressWarnings("unused") PythonObject object) { @Idempotent protected static boolean hasNoDict(Shape shape) { - return (shape.getFlags() & PythonObject.HAS_MATERIALIZED_DICT) == 0; + return (shape.getFlags() & PythonObject.HAS_DICT) == 0; } @Specialization(guards = {"isSingleContext()", "object == cached", "dictIsConstant(cached)", "dict != null"}, limit = "1") @@ -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)); + 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/GetForeignObjectClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java index 36fcaa99da..0d63dfa5b9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetForeignObjectClassNode.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 @@ -95,8 +95,12 @@ public enum Trait { // Interop types first as they are the most concrete/specific types NULL("None", PythonBuiltinClassType.PNone), BOOLEAN("Boolean", PythonBuiltinClassType.ForeignBoolean), + DATE("Date", PythonBuiltinClassType.ForeignDate), + DATETIME("DateTime", PythonBuiltinClassType.ForeignDateTime), NUMBER("Number", PythonBuiltinClassType.ForeignNumber), // int, float, complex STRING("String", PythonBuiltinClassType.PString), + TIME("Time", PythonBuiltinClassType.ForeignTime), + TIME_ZONE("TimeZone", PythonBuiltinClassType.ForeignTimeZone), EXCEPTION("Exception", PythonBuiltinClassType.PBaseException), META_OBJECT("AbstractClass", PythonBuiltinClassType.ForeignAbstractClass), @@ -154,9 +158,16 @@ PythonManagedClass uncached(Object object, } protected static int getTraits(Object object, InteropLibrary interop) { + // Temporal types are a bit special since some traits should exclude each other to match + // the split in Python's datetime module + boolean isDate = interop.isDate(object); + boolean isTime = interop.isTime(object); + boolean isTimeZone = interop.isTimeZone(object); // Alphabetic order here as it does not matter return (interop.hasArrayElements(object) ? Trait.ARRAY.bit : 0) + (interop.isBoolean(object) ? Trait.BOOLEAN.bit : 0) + + (isDate && isTime ? Trait.DATETIME.bit : 0) + + (isDate && !isTime ? Trait.DATE.bit : 0) + (interop.isException(object) ? Trait.EXCEPTION.bit : 0) + (interop.isExecutable(object) ? Trait.EXECUTABLE.bit : 0) + (interop.hasHashEntries(object) ? Trait.HASH.bit : 0) + @@ -166,7 +177,9 @@ protected static int getTraits(Object object, InteropLibrary interop) { (interop.isMetaObject(object) ? Trait.META_OBJECT.bit : 0) + (interop.isNull(object) ? Trait.NULL.bit : 0) + (interop.isNumber(object) ? Trait.NUMBER.bit : 0) + - (interop.isString(object) ? Trait.STRING.bit : 0); + (interop.isString(object) ? Trait.STRING.bit : 0) + + (!isDate && isTime ? Trait.TIME.bit : 0) + + (!isDate && !isTime && isTimeZone ? Trait.TIME_ZONE.bit : 0); } private PythonManagedClass classForTraits(PythonContext context, int traits) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsNode.java index 9f602b2483..9bb8643a2f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsNode.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,22 +43,16 @@ import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; -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.ints.PInt; import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.nodes.PGuards; -import com.oracle.graal.python.nodes.bytecode.PBytecodeGeneratorFunctionRootNode; -import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; -import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode; import com.oracle.graal.python.nodes.expression.BinaryOp; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsAnyBuiltinObjectProfile; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.util.OverflowException; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.bytecode.OperationProxy; import com.oracle.truffle.api.bytecode.StoreBytecodeIndex; import com.oracle.truffle.api.dsl.Bind; @@ -75,11 +69,10 @@ 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.nodes.RootNode; @ImportStatic(PythonOptions.class) @GenerateUncached -@OperationProxy.Proxyable(storeBytecodeIndex = false) +@OperationProxy.Proxyable(allowUncached = true, storeBytecodeIndex = false) @GenerateInline(false) // footprint reduction 44 -> 26 public abstract class IsNode extends Node implements BinaryOp { @@ -222,44 +215,6 @@ public static boolean doNative(PythonAbstractNativeObject left, PythonAbstractNa return interop.isIdentical(left, right, interop); } - // code - @Specialization - @InliningCutoff - public static boolean doCode(PCode left, PCode right, - @Bind Node inliningTarget, - @Cached CodeNodes.GetCodeCallTargetNode getCt) { - // Special case for code objects: Frames create them on-demand even if they refer to the - // same function. So we need to compare the root nodes. - if (left != right) { - RootCallTarget leftCt = getCt.execute(inliningTarget, left); - RootCallTarget rightCt = getCt.execute(inliningTarget, right); - if (leftCt != null && rightCt != null) { - RootNode leftRootNode = leftCt.getRootNode(); - RootNode rightRootNode = rightCt.getRootNode(); - if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { - if (leftRootNode instanceof PBytecodeDSLRootNode l && rightRootNode instanceof PBytecodeDSLRootNode r) { - return l.getCodeUnit() == r.getCodeUnit(); - } - } else { - if (leftRootNode instanceof PBytecodeGeneratorFunctionRootNode l) { - leftRootNode = l.getBytecodeRootNode(); - } - if (rightRootNode instanceof PBytecodeGeneratorFunctionRootNode r) { - rightRootNode = r.getBytecodeRootNode(); - } - - if (leftRootNode instanceof PBytecodeRootNode l && rightRootNode instanceof PBytecodeRootNode r) { - return l.getCodeUnit() == r.getCodeUnit(); - } - } - return leftRootNode == rightRootNode; - } else { - return false; - } - } - return true; - } - public static boolean someIsNone(Object left, Object right) { return PGuards.isPNone(left) || PGuards.isPNone(right); } 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..0b093b39fa 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); + long dictPointer = dictToNative.execute(inliningTarget, dict); + 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/statement/AbstractImportNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/statement/AbstractImportNode.java index 7bc12b1cfc..212c1cde89 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/statement/AbstractImportNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/statement/AbstractImportNode.java @@ -184,19 +184,19 @@ protected final Object importModule(VirtualFrame frame, TruffleString name, Obje * what it's set to in the frame and globals. */ @GenerateUncached - @GenerateInline(false) // footprint reduction 48 -> 29 + @GenerateInline(false) public abstract static class ImportName extends Node { public abstract Object execute(Frame frame, PythonContext context, PythonModule builtins, TruffleString name, Object globals, TruffleString[] fromList, int level); @Specialization static Object importName(VirtualFrame frame, PythonContext context, PythonModule builtins, TruffleString name, Object globals, TruffleString[] fromList, int level, - @Cached ReadAttributeFromPythonObjectNode readAttrNode, @Bind Node inliningTarget, + @Cached(inline = true) ReadAttributeFromPythonObjectNode readAttrNode, @Cached InlinedConditionProfile importFuncProfile, @Cached PConstructAndRaiseNode.Lazy raiseNode, @Cached CallNode importCallNode, @Cached PyImportImportModuleLevelObject importModuleLevel) { - Object importFunc = readAttrNode.execute(builtins, T___IMPORT__, null); + Object importFunc = readAttrNode.execute(inliningTarget, builtins, T___IMPORT__, null); if (importFunc == null) { throw raiseNode.get(inliningTarget).raiseImportError(frame, IMPORT_NOT_FOUND); } 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 5a92f153d6..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,12 +41,13 @@ 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; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode; @@ -122,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; @@ -149,7 +149,11 @@ public static Double doInterop(Object obj, return null; } - @Specialization(guards = "!isNumber(obj)") + static boolean isInteropObject(Object obj) { + return !PGuards.isNumber(obj) && !(obj instanceof PythonAbstractNativeObject); + } + + @Specialization(guards = "isInteropObject(obj)") @InliningCutoff static double doGeneric(Object obj, @CachedLibrary(limit = "3") InteropLibrary interopLibrary) { 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 b0d2b72d56..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 @@ -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,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; @@ -72,7 +74,6 @@ 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.strings.TruffleString.Encoding; /** * Casts a Python string to a TruffleString without coercion. ATTENTION: If the cast fails, @@ -135,40 +136,32 @@ 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, - @Cached TruffleString.FromNativePointerNode fromNative, - @Cached TruffleString.FromByteArrayNode fromBytes) { - int state = readI32.read(pointer, PyASCIIObject__state); + static TruffleString read(long rawPointer, + @Cached TruffleString.FromNativePointerWithCompactionUTF32Node fromNative, + @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); - Encoding encoding; + TruffleString.CompactionLevel compactionLevel; if (kind == 1) { // isBitSet(state, PyASCIIObject__state_ascii_shift)) // ascii doesn't matter, codepoint 0-127 are the same in ascii and latin1 - encoding = Encoding.ISO_8859_1; + compactionLevel = TruffleString.CompactionLevel.S1; } else if (kind == 2) { - encoding = Encoding.UTF_16LE; + compactionLevel = TruffleString.CompactionLevel.S2; } else { assert kind == 4; - encoding = Encoding.UTF_32LE; + compactionLevel = TruffleString.CompactionLevel.S4; } int bytes = PythonUtils.toIntError(length * kind); - if (lib.isPointer(data) || data instanceof Long) { - return fromNative.execute(data, 0, bytes, encoding, false); - } - byte[] result = readByte.readByteArray(data, bytes); - return fromBytes.execute(result, encoding, 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/nodes/util/CoerceToComplexNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CoerceToComplexNode.java index 848adfa7c2..7ca81a500c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CoerceToComplexNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CoerceToComplexNode.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 @@ -43,7 +43,6 @@ import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.complex.PComplex; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.nodes.ErrorMessages; @@ -51,6 +50,7 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; +import com.oracle.graal.python.nodes.call.special.SpecialMethodNotFound; import com.oracle.graal.python.nodes.truffle.PythonIntegerAndFloatTypes; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; @@ -94,8 +94,8 @@ static PComplex toComplex(VirtualFrame frame, Node inliningTarget, Object x, @Cached PyFloatAsDoubleNode asDoubleNode, @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { - Object result = callComplexFunc.executeObject(frame, x); - if (result != PNone.NO_VALUE) { + try { + Object result = callComplexFunc.executeObject(frame, x); if (result instanceof PComplex) { // TODO we need pass here deprecation warning // DeprecationWarning: __complex__ returned non-complex (type %p). @@ -103,10 +103,10 @@ static PComplex toComplex(VirtualFrame frame, Node inliningTarget, Object x, // deprecated, // and may be removed in a future version of Python. return (PComplex) result; - } else { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.SHOULD_RETURN, "__complex__", "complex object"); } + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.SHOULD_RETURN, "__complex__", "complex object"); + } catch (SpecialMethodNotFound e) { + return PFactory.createComplex(language, asDoubleNode.execute(frame, inliningTarget, x), 0); } - return PFactory.createComplex(language, asDoubleNode.execute(frame, inliningTarget, x), 0); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/ExceptionStateNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/ExceptionStateNodes.java index 94247b0065..d649e5d5c4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/ExceptionStateNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/ExceptionStateNodes.java @@ -79,9 +79,17 @@ public abstract class ExceptionStateNodes { public static final class GetCaughtExceptionNode extends Node { @Child private GetThreadStateNode getThreadStateNode; - private final ConditionProfile nullFrameProfile = ConditionProfile.create(); - private final ConditionProfile hasExceptionProfile = ConditionProfile.create(); - private final ConditionProfile needsStackWalkProfile = ConditionProfile.create(); + private final ConditionProfile nullFrameProfile; + private final ConditionProfile hasExceptionProfile; + private final ConditionProfile needsStackWalkProfile; + + private GetCaughtExceptionNode(ConditionProfile nullFrameProfile, ConditionProfile hasExceptionProfile, + ConditionProfile needsStackWalkProfile, GetThreadStateNode getThreadStateNode) { + this.nullFrameProfile = nullFrameProfile; + this.hasExceptionProfile = hasExceptionProfile; + this.needsStackWalkProfile = needsStackWalkProfile; + this.getThreadStateNode = getThreadStateNode; + } public AbstractTruffleException execute(VirtualFrame frame) { if (nullFrameProfile.profile(frame == null)) { @@ -190,7 +198,14 @@ private AbstractTruffleException ensure(AbstractTruffleException e) { @NeverDefault public static GetCaughtExceptionNode create() { - return new GetCaughtExceptionNode(); + return new GetCaughtExceptionNode(ConditionProfile.create(), ConditionProfile.create(), ConditionProfile.create(), null); + } + + private static final GetCaughtExceptionNode UNCACHED = new GetCaughtExceptionNode(ConditionProfile.getUncached(), ConditionProfile.getUncached(), ConditionProfile.getUncached(), + GetThreadStateNode.getUncached()); + + public static GetCaughtExceptionNode getUncached() { + return UNCACHED; } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/AsyncHandler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/AsyncHandler.java index 82e97615d9..58bebd4dd5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/AsyncHandler.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/AsyncHandler.java @@ -54,6 +54,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.graalvm.polyglot.SandboxPolicy; @@ -73,7 +74,6 @@ import com.oracle.graal.python.util.SuppressFBWarnings; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.ThreadLocalAction; import com.oracle.truffle.api.ThreadLocalAction.Access; import com.oracle.truffle.api.TruffleLanguage; @@ -201,7 +201,7 @@ public final void execute(PythonContext context, Access access) { } } try { - CallDispatchers.SimpleIndirectInvokeNode.executeUncached(context.getAsyncHandler().callTarget, args); + CallDispatchers.SimpleIndirectInvokeNode.executeUncached(context.getAsyncHandler().getCallTarget(), args); } catch (PException e) { handleException(e); } finally { @@ -341,11 +341,11 @@ public boolean setsUpCalleeContext() { } } - private final RootCallTarget callTarget; + private final CallRootNode rootNode; AsyncHandler(PythonContext context) { this.context = new WeakReference<>(context); - this.callTarget = context.getLanguage().createCachedCallTarget(CallRootNode::new, CallRootNode.class); + this.rootNode = context.getLanguage().createCachedRootNode(CallRootNode::new, CallRootNode.class); if (PythonOptions.AUTOMATIC_ASYNC_ACTIONS) { this.executorService = Executors.newScheduledThreadPool(6, runnable -> { Thread t = Executors.defaultThreadFactory().newThread(runnable); @@ -380,6 +380,10 @@ void registerAction(Supplier actionSupplier) { } } + private com.oracle.truffle.api.RootCallTarget getCallTarget() { + return rootNode.getCallTarget(); + } + void poll() { if (!PythonOptions.AUTOMATIC_ASYNC_ACTIONS) { for (AsyncRunnable r : registeredActions) { @@ -507,7 +511,7 @@ public SharedFinalizer(PythonContext context) { */ public abstract static class FinalizableReference extends PhantomReference { private final Object reference; - private boolean released; + private final AtomicBoolean released = new AtomicBoolean(false); @SuppressWarnings("this-escape") public FinalizableReference(Object referent, Object reference, SharedFinalizer sharedFinalizer) { @@ -517,6 +521,13 @@ public FinalizableReference(Object referent, Object reference, SharedFinalizer s addLiveReference(sharedFinalizer, this); } + @SuppressWarnings("this-escape") + protected FinalizableReference(Object referent, SharedFinalizer sharedFinalizer) { + super(referent, sharedFinalizer.queue); + this.reference = null; + addLiveReference(sharedFinalizer, this); + } + /** * We'll keep a reference for the FinalizableReference object until the async handler * schedule the collect process. @@ -535,15 +546,17 @@ public final Object getReference() { } public final boolean isReleased() { - return released; + return released.get(); } /** - * Mark the FinalizableReference as freed in case it has been freed elsewhare. This will + * Mark the FinalizableReference as freed in case it has been freed elsewhere. This will * avoid double-freeing the reference. */ - public final void markReleased() { - this.released = true; + public final boolean markReleased() { + boolean markedReleased = released.compareAndSet(false, true); + assert markedReleased || released.get(); + return markedReleased; } /** diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java index cd9bbdd9ac..1e1b09297d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.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 @@ -44,6 +44,7 @@ import static com.oracle.graal.python.annotations.PythonOS.PLATFORM_LINUX; import static com.oracle.graal.python.annotations.PythonOS.PLATFORM_WIN32; import static com.oracle.graal.python.builtins.modules.SignalModuleBuiltins.signalFromName; +import static com.oracle.graal.python.builtins.modules.SignalModuleBuiltins.triggerEmulatedSignal; import static com.oracle.graal.python.builtins.objects.thread.PThread.getThreadId; import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; import static com.oracle.graal.python.nodes.StringLiterals.T_JAVA; @@ -209,6 +210,9 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -233,6 +237,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet6SockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException; import com.oracle.graal.python.runtime.PosixSupportLibrary.OpenPtyResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult; import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult; @@ -349,6 +354,10 @@ public final class EmulatedPosixSupport extends PosixResources { private final boolean withoutIOSocket; // Lazily parsed content of /etc/services. private Map> etcServices; + private ScheduledExecutorService itimerService; + private ScheduledFuture itimerFuture; + private long itimerInterval; + private Alarm currentAlarm; public EmulatedPosixSupport(PythonContext context) { super(context); @@ -996,7 +1005,7 @@ public void setBlocking(int fd, boolean blocking, // Already blocking return; } - throw new PosixException(OSErrorEnum.EPERM.getNumber(), toTruffleStringUncached("Emulated posix support does not support non-blocking mode for regular files.")); + throw new PosixErrnoException(OSErrorEnum.EPERM.getNumber(), toTruffleStringUncached("Emulated posix support does not support non-blocking mode for regular files.")); } } catch (PosixException e) { throw e; @@ -1137,10 +1146,10 @@ public long[] statvfs(Object path) throws PosixException { return new long[]{bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax, fsid}; } catch (NoSuchFileException e) { - throw new PosixException(OSErrorEnum.ENOENT.getNumber(), ErrorMessages.NO_SUCH_FILE_OR_DIR); + throw new PosixErrnoException(OSErrorEnum.ENOENT.getNumber(), ErrorMessages.NO_SUCH_FILE_OR_DIR); } catch (UnsupportedOperationException | IOException | SecurityException e) { TruffleString message = PythonUtils.toTruffleStringUncached(e.getMessage()); - throw new PosixException(OSErrorEnum.EPERM.getNumber(), message); + throw new PosixErrnoException(OSErrorEnum.EPERM.getNumber(), message); } } @@ -1150,7 +1159,7 @@ public long[] fstatvfs(int fd) throws PosixException { String path = getFilePath(fd); if (path == null) { - throw new PosixException(OSErrorEnum.EBADF.getNumber(), ErrorMessages.BAD_FILE_DESCRIPTOR); + throw new PosixErrnoException(OSErrorEnum.EBADF.getNumber(), ErrorMessages.BAD_FILE_DESCRIPTOR); } return statvfs(path); @@ -1885,7 +1894,7 @@ public boolean faccessat(int dirFd, Object path, int mode, boolean effectiveIds, @Shared("defaultDirProfile") @Cached InlinedConditionProfile defaultDirFdPofile, @Shared("eq") @Cached TruffleString.EqualNode eqNode, @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode, - @Shared("ts2js") @Cached TruffleString.ToJavaStringNode toJavaStringNode) { + @Shared("ts2js") @Cached TruffleString.ToJavaStringNode toJavaStringNode) throws UnsupportedPosixFeatureException { if (effectiveIds) { errBranch.enter(inliningTarget); throw createUnsupportedFeature("faccess with effective user IDs"); @@ -1965,12 +1974,12 @@ public void fchmod(int fd, int mode, } @ExportMessage - public void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks) { + public void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks) throws PosixException { throw createUnsupportedFeature("fchownat"); } @ExportMessage - public void fchown(int fd, long owner, long group) { + public void fchown(int fd, long owner, long group) throws PosixException { throw createUnsupportedFeature("fchown"); } @@ -2016,7 +2025,145 @@ public void kill(long pid, int signal, @ExportMessage @SuppressWarnings("static-method") - public long killpg(long pgid, int signal) { + public void raise(int signal) throws PosixException { + throw createUnsupportedFeature("raise"); + } + + @ExportMessage + @TruffleBoundary + public synchronized int alarm(int seconds) { + int remaining = 0; + if (currentAlarm != null && currentAlarm.isRunning()) { + remaining = currentAlarm.getRemainingSeconds(); + if (remaining < 0) { + remaining = 0; + } + currentAlarm.cancel(); + } + if (seconds > 0) { + currentAlarm = new Alarm(context, seconds); + currentAlarm.start(); + } else { + currentAlarm = null; + } + return remaining; + } + + @ExportMessage + @TruffleBoundary + public synchronized Timeval[] getitimer(int which) throws PosixException { + if (which != 0) { + throw posixException(OSErrorEnum.EINVAL); + } + return currentItimer(); + } + + @ExportMessage + @TruffleBoundary + public synchronized Timeval[] setitimer(int which, Timeval delay, Timeval interval) throws PosixException { + if (which != 0 || delay.getSeconds() < 0 || delay.getMicroseconds() < 0 || interval.getSeconds() < 0 || interval.getMicroseconds() < 0) { + throw posixException(OSErrorEnum.EINVAL); + } + Timeval[] oldValue = currentItimer(); + long usDelay = toMicroseconds(delay); + long usInterval = toMicroseconds(interval); + if (itimerFuture != null) { + itimerFuture.cancel(false); + itimerFuture = null; + itimerInterval = 0; + } + if (usDelay != 0) { + itimerInterval = usInterval; + Runnable raiseAlarm = () -> triggerEmulatedSignal(context, "ALRM"); + if (usInterval == 0) { + itimerFuture = getItimerService().schedule(raiseAlarm, usDelay, TimeUnit.MICROSECONDS); + } else { + itimerFuture = getItimerService().scheduleAtFixedRate(raiseAlarm, usDelay, usInterval, TimeUnit.MICROSECONDS); + } + } + return oldValue; + } + + private Timeval[] currentItimer() { + return new Timeval[]{fromMicroseconds(currentItimerDelay()), fromMicroseconds(itimerInterval)}; + } + + private long currentItimerDelay() { + if (itimerFuture == null) { + return 0; + } + long delay = itimerFuture.getDelay(TimeUnit.MICROSECONDS); + return Math.max(delay, 0); + } + + private ScheduledExecutorService getItimerService() { + if (itimerService == null) { + ScheduledExecutorService newItimerService = Executors.newSingleThreadScheduledExecutor(runnable -> { + Thread thread = Executors.defaultThreadFactory().newThread(runnable); + thread.setDaemon(true); + return thread; + }); + itimerService = newItimerService; + context.registerAtexitHook(ctx -> newItimerService.shutdown()); + } + return itimerService; + } + + private static long toMicroseconds(Timeval timeval) { + return TimeUnit.SECONDS.toMicros(timeval.getSeconds()) + timeval.getMicroseconds(); + } + + private static Timeval fromMicroseconds(long microseconds) { + return new Timeval(microseconds / TimeUnit.SECONDS.toMicros(1), microseconds % TimeUnit.SECONDS.toMicros(1)); + } + + private static final class Alarm { + private final PythonContext context; + private final int seconds; + private final long startMillis; + private Thread thread; + + Alarm(PythonContext context, int seconds) { + this.context = context; + this.seconds = seconds; + this.startMillis = System.currentTimeMillis(); + } + + void start() { + thread = new Thread(() -> { + try { + Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); + triggerEmulatedSignal(context, "ALRM"); + } catch (InterruptedException e) { + // Cancelled + } + }); + thread.setDaemon(true); + thread.start(); + } + + boolean isRunning() { + return thread.isAlive(); + } + + void cancel() { + thread.interrupt(); + } + + int getRemainingSeconds() { + return seconds - (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - startMillis); + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public void signalSelf(int signal) throws PosixException { + throw createUnsupportedFeature("signalSelf"); + } + + @ExportMessage + @SuppressWarnings("static-method") + public long killpg(long pgid, int signal) throws PosixException { throw createUnsupportedFeature("killpg"); } @@ -2123,7 +2270,7 @@ public long getuid() { @ExportMessage @SuppressWarnings("static-method") @TruffleBoundary - public long geteuid() { + public long geteuid() throws UnsupportedPosixFeatureException { throw createUnsupportedFeature("geteuid"); } @@ -2146,49 +2293,49 @@ public long getgid() { @ExportMessage @SuppressWarnings("static-method") @TruffleBoundary - public long getegid() { + public long getegid() throws UnsupportedPosixFeatureException { throw createUnsupportedFeature("getegid"); } @ExportMessage @SuppressWarnings("static-method") - public long getppid() { + public long getppid() throws UnsupportedPosixFeatureException { throw createUnsupportedFeature("getppid"); } @ExportMessage @SuppressWarnings("static-method") - public long getpgid(long pid) { + public long getpgid(long pid) throws PosixException { throw createUnsupportedFeature("getpgid"); } @ExportMessage @SuppressWarnings("static-method") - public void setpgid(long pid, long pgid) { + public void setpgid(long pid, long pgid) throws PosixException { throw createUnsupportedFeature("setpgid"); } @ExportMessage @SuppressWarnings("static-method") - public long getpgrp() { + public long getpgrp() throws UnsupportedPosixFeatureException { throw createUnsupportedFeature("getpgrp"); } @ExportMessage @SuppressWarnings("static-method") - public long getsid(long pid) { + public long getsid(long pid) throws PosixException { throw createUnsupportedFeature("getsid"); } @ExportMessage @SuppressWarnings("static-method") - public long setsid() { + public long setsid() throws PosixException { throw createUnsupportedFeature("getsid"); } @ExportMessage @TruffleBoundary - public long[] getgroups() { + public long[] getgroups() throws PosixException { if (!PythonImageBuildOptions.WITHOUT_PLATFORM_ACCESS) { switch (PythonLanguage.getPythonOS()) { case PLATFORM_LINUX, PLATFORM_DARWIN -> { @@ -2266,7 +2413,7 @@ public RusageResult getrusage(int who) throws PosixException { @ExportMessage @SuppressWarnings("static-method") - public OpenPtyResult openpty() { + public OpenPtyResult openpty() throws PosixException { throw createUnsupportedFeature("openpty"); } @@ -2298,11 +2445,14 @@ public void unsetenv(Object name) { @ExportMessage @TruffleBoundary public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd, - int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, + int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, boolean allowVFork, @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode) throws PosixException { if (PythonImageBuildOptions.WITHOUT_PLATFORM_ACCESS) { throw new UnsupportedPosixFeatureException("forkExec was excluded"); } + if (pgidToSet >= 0) { + throw createUnsupportedFeature("process_group in fork_exec"); + } // TODO there are a few arguments we ignore, we should throw an exception or report a // compatibility warning @@ -2712,7 +2862,7 @@ final MMapHandle mmap(long length, int prot, int flags, int fd, long offset, } @TruffleBoundary - private static Set mmapProtToOptions(int prot) { + private static Set mmapProtToOptions(int prot) throws PosixException { HashSet options = new HashSet<>(); if ((prot & PROT_READ.value) != 0) { options.add(StandardOpenOption.READ); @@ -2855,7 +3005,7 @@ public void mmapUnmap(Object mmap, @SuppressWarnings("unused") long length, @ExportMessage @SuppressWarnings("static-method") - public long mmapGetPointer(@SuppressWarnings("unused") Object mmap) { + public long mmapGetPointer(@SuppressWarnings("unused") Object mmap) throws UnsupportedPosixFeatureException { throw createUnsupportedFeature("obtaining mmap pointer"); } @@ -2969,7 +3119,7 @@ public PwdResult getpwuid(long uid) throws PosixException { @ExportMessage @TruffleBoundary @SuppressWarnings("static-method") - public PwdResult getpwnam(Object name) { + public PwdResult getpwnam(Object name) throws PosixException { if (!PythonImageBuildOptions.WITHOUT_PLATFORM_ACCESS) { switch (PythonLanguage.getPythonOS()) { case PLATFORM_LINUX: @@ -2995,7 +3145,7 @@ public boolean hasGetpwentries() { @ExportMessage @SuppressWarnings("static-method") - public PwdResult[] getpwentries() { + public PwdResult[] getpwentries() throws PosixException { throw createUnsupportedFeature("getpwent"); } @@ -3006,13 +3156,13 @@ private static PwdResult createPwdResult(UnixSystem unix) { @ExportMessage @SuppressWarnings("unused") - public int ioctlBytes(int fd, long request, byte[] arg) { + public int ioctlBytes(int fd, long request, byte[] arg) throws PosixException { throw createUnsupportedFeature("ioctl"); } @ExportMessage @SuppressWarnings("unused") - public int ioctlInt(int fd, long request, int arg) { + public int ioctlInt(int fd, long request, int arg) throws PosixException { throw createUnsupportedFeature("ioctl"); } @@ -3452,7 +3602,7 @@ public Object gethostname() throws PosixException { @ExportMessage @SuppressWarnings("static-method") @TruffleBoundary - public Object[] getnameinfo(UniversalSockAddr addr, int flags) throws GetAddrInfoException { + public Object[] getnameinfo(UniversalSockAddr addr, int flags) throws UnsupportedPosixFeatureException, GetAddrInfoException { if (PythonImageBuildOptions.WITHOUT_JAVA_INET || withoutIOSocket) { throw new UnsupportedPosixFeatureException("getnameinfo was excluded"); } @@ -3503,7 +3653,7 @@ private String searchServicesForPort(TruffleLanguage.Env env, int port, String p @ExportMessage @SuppressWarnings("static-method") @TruffleBoundary - public AddrInfoCursor getaddrinfo(Object node, Object service, int family, int sockType, int protocol, int flags) throws GetAddrInfoException { + public AddrInfoCursor getaddrinfo(Object node, Object service, int family, int sockType, int protocol, int flags) throws UnsupportedPosixFeatureException, GetAddrInfoException { if (PythonImageBuildOptions.WITHOUT_JAVA_INET || withoutIOSocket) { throw new UnsupportedPosixFeatureException("getaddrinfo was excluded"); } @@ -3695,7 +3845,7 @@ UniversalSockAddr createUniversalSockAddrInet6(Inet6SockAddr src) { } @ExportMessage - UniversalSockAddr createUniversalSockAddrUnix(UnixSockAddr src) { + UniversalSockAddr createUniversalSockAddrUnix(UnixSockAddr src) throws UnsupportedPosixFeatureException { throw createUnsupportedFeature("AF_UNIX"); } @@ -4465,7 +4615,7 @@ private static GetAddrInfoException gaiError(int err) throws GetAddrInfoExceptio } static PosixException posixException(OSErrorEnum osError) throws PosixException { - throw new PosixException(osError.getNumber(), osError.getMessage()); + throw new PosixErrnoException(osError.getNumber(), osError.getMessage()); } private static PosixException posixException(Exception e, TruffleString.EqualNode eqNode) throws PosixException { @@ -4473,11 +4623,11 @@ private static PosixException posixException(Exception e, TruffleString.EqualNod throw (PosixException) e; } ErrorAndMessagePair pair = OSErrorEnum.fromException(e, eqNode); - throw new PosixException(pair.oserror.getNumber(), pair.message); + throw new PosixErrnoException(pair.oserror.getNumber(), pair.message); } private static PosixException posixException(ErrorAndMessagePair pair) throws PosixException { - throw new PosixException(pair.oserror.getNumber(), pair.message); + throw new PosixErrnoException(pair.oserror.getNumber(), pair.message); } @TruffleBoundary 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 08b79def03..4463b8b10b 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 @@ -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 @@ -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 @@ -181,7 +184,7 @@ protected static void prepareCall(VirtualFrame frame, Object[] callArguments, in @Bind Node inliningTarget, @Cached PassCallerFrameNode passCallerFrame, @Cached PassExceptionStateNode passExceptionState) { - assert PArguments.isPythonFrame(frame) || inliningTarget.getRootNode() instanceof TopLevelExceptionHandler : "calling from non-Python or non-top-level frame"; + assert inliningTarget.getRootNode() instanceof TopLevelExceptionHandler || PArguments.assertIsPythonFrame(frame) : "calling from non-Python or non-top-level frame"; passCallerFrame.execute(frame, inliningTarget, callArguments, callerFlags); passExceptionState.execute(frame, inliningTarget, callArguments, CallerFlags.needsExceptionState(callerFlags)); } @@ -210,15 +213,6 @@ protected static void passCallerFrame(VirtualFrame frame, Object[] callArguments // We are handing the PFrame of the current frame to the caller, i.e., it does // not 'escape' since it is still on the stack. Also, force synchronization of // values if requested - if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER) { - // For the manual interpreter it is OK to executeOnStack with uncached - // `materialize` Node, but once we have uncached Bytecode DSL interpreter, - // it - // will have to use EncapsulatingNodeReference or some other way (e.g., in - // frame) to pass down the BytecodeNode. This can be also sign of missing - // BoundaryCallContext.enter/exit around TruffleBoundary - assert materialize.isAdoptable(); - } if (thisInfo.getPyFrame() != null && !CallerFlags.needsLocals(callerFlags) && !CallerFlags.needsLasti(callerFlags)) { thisInfo.getPyFrame().setLastCallerFlags(callerFlags); } else { @@ -442,7 +436,10 @@ private void exitEscaped(VirtualFrame frame, PRootNode node, Node location, Refe CompilerDirectives.transferToInterpreterAndInvalidate(); everEscaped = true; } - // This assumption acts as our branch profile here + forceEscapeFrame(frame, location, info, ensureMaterializeNode()); + } + + public static void forceEscapeFrame(Frame frame, Node location, Reference info, MaterializeFrameNode materializeNode) { Reference callerInfo = info.getCallerInfo(); if (callerInfo == null) { // we didn't request the caller frame reference. now we need it. @@ -466,11 +463,7 @@ private void exitEscaped(VirtualFrame frame, PRootNode node, Node location, Refe // initializations (importing a module) from invalidating the assumption // force the frame so that it can be accessed later - if (PythonOptions.ENABLE_BYTECODE_DSL_INTERPRETER && node instanceof PBytecodeDSLRootNode) { - ensureMaterializeNode().executeOnStack(frame, (BytecodeNode) location, false, true); - } else { - ensureMaterializeNode().executeOnStack(frame, node, false, true); - } + materializeNode.execute(location, false, true, frame); // if this frame escaped we must ensure that also f_back does callerInfo.markAsEscaped(); info.setCallerInfo(callerInfo); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java index 3ee34664e8..396f647f60 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.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 @@ -55,6 +55,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException; import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidUnixSocketPathException; import com.oracle.graal.python.runtime.PosixSupportLibrary.OpenPtyResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult; import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult; @@ -63,6 +64,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; @@ -681,9 +683,13 @@ final void renameat(int oldDirFd, Object oldPath, int newDirFd, Object newPath, @ExportMessage final boolean faccessat(int dirFd, Object path, int mode, boolean effectiveIds, boolean followSymlinks, - @CachedLibrary("this.delegate") PosixSupportLibrary lib) { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException { logEnter("faccessAt", "%d, %s, 0%o, %b, %b", dirFd, path, mode, effectiveIds, followSymlinks); - return logExit("faccessAt", "%b", lib.faccessat(delegate, dirFd, path, mode, effectiveIds, followSymlinks)); + try { + return logExit("faccessAt", "%b", lib.faccessat(delegate, dirFd, path, mode, effectiveIds, followSymlinks)); + } catch (UnsupportedPosixFeatureException e) { + throw logException("faccessAt", e); + } } @ExportMessage @@ -752,6 +758,61 @@ final void kill(long pid, int signal, } } + @ExportMessage + final void raise(int signal, + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { + logEnter("raise", "%d", signal); + try { + lib.raise(delegate, signal); + } catch (PosixException e) { + throw logException("raise", e); + } + } + + @ExportMessage + final int alarm(int seconds, + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { + logEnter("alarm", "%d", seconds); + try { + return logExit("alarm", "%d", lib.alarm(delegate, seconds)); + } catch (PosixException e) { + throw logException("alarm", e); + } + } + + @ExportMessage + final Timeval[] getitimer(int which, + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { + logEnter("getitimer", "%d", which); + try { + return logExit("getitimer", "%s", lib.getitimer(delegate, which)); + } catch (PosixException e) { + throw logException("getitimer", e); + } + } + + @ExportMessage + final Timeval[] setitimer(int which, Timeval delay, Timeval interval, + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { + logEnter("setitimer", "%d, %s, %s", which, delay, interval); + try { + return logExit("setitimer", "%s", lib.setitimer(delegate, which, delay, interval)); + } catch (PosixException e) { + throw logException("setitimer", e); + } + } + + @ExportMessage + final void signalSelf(int signal, + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { + logEnter("signalSelf", "%d", signal); + try { + lib.signalSelf(delegate, signal); + } catch (PosixException e) { + throw logException("signalSelf", e); + } + } + @ExportMessage final void killpg(long pgid, int signal, @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { @@ -872,9 +933,13 @@ final long getuid( @ExportMessage final long geteuid( - @CachedLibrary("this.delegate") PosixSupportLibrary lib) { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException { logEnter("geteuid", ""); - return logExit("geteuid", "%d", lib.geteuid(delegate)); + try { + return logExit("geteuid", "%d", lib.geteuid(delegate)); + } catch (UnsupportedPosixFeatureException e) { + throw logException("geteuid", e); + } } @ExportMessage @@ -886,16 +951,24 @@ final long getgid( @ExportMessage final long getegid( - @CachedLibrary("this.delegate") PosixSupportLibrary lib) { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException { logEnter("getegid", ""); - return logExit("getegid", "%d", lib.getegid(delegate)); + try { + return logExit("getegid", "%d", lib.getegid(delegate)); + } catch (UnsupportedPosixFeatureException e) { + throw logException("getegid", e); + } } @ExportMessage final long getppid( - @CachedLibrary("this.delegate") PosixSupportLibrary lib) { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException { logEnter("getppid", ""); - return logExit("getppid", "%d", lib.getppid(delegate)); + try { + return logExit("getppid", "%d", lib.getppid(delegate)); + } catch (UnsupportedPosixFeatureException e) { + throw logException("getppid", e); + } } @ExportMessage @@ -922,9 +995,13 @@ final void setpgid(long pid, long pgid, @ExportMessage final long getpgrp( - @CachedLibrary("this.delegate") PosixSupportLibrary lib) { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException { logEnter("getpgrp", ""); - return logExit("getpgrp", "%d", lib.getpgrp(delegate)); + try { + return logExit("getpgrp", "%d", lib.getpgrp(delegate)); + } catch (UnsupportedPosixFeatureException e) { + throw logException("getpgrp", e); + } } @ExportMessage @@ -1037,13 +1114,14 @@ public void mmapWriteBytes(Object mmap, long index, byte[] bytes, int length, @ExportMessage final int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd, - int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, + int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, boolean allowVFork, @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { - logEnter("forkExec", "%s, %s, %s, %s, %d, %d, %d, %d, %d, %d, %d, %d, %b, %b, %b, %s", executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd, - stderrWriteFd, errPipeReadFd, errPipeWriteFd, closeFds, restoreSignals, callSetsid, fdsToKeep); + logEnter("forkExec", "%s, %s, %s, %s, %d, %d, %d, %d, %d, %d, %d, %d, %b, %b, %b, %d, %s, %b", executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, + stderrReadFd, + stderrWriteFd, errPipeReadFd, errPipeWriteFd, closeFds, restoreSignals, callSetsid, pgidToSet, fdsToKeep, allowVFork); try { return logExit("forkExec", "%d", lib.forkExec(delegate, executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd, stderrWriteFd, errPipeReadFd, - errPipeWriteFd, closeFds, restoreSignals, callSetsid, fdsToKeep)); + errPipeWriteFd, closeFds, restoreSignals, callSetsid, pgidToSet, fdsToKeep, allowVFork)); } catch (PosixException e) { throw logException("forkExec", e); } @@ -1062,9 +1140,13 @@ public void mmapFlush(Object mmap, long offset, long length, @ExportMessage public long mmapGetPointer(Object mmap, - @CachedLibrary("this.delegate") PosixSupportLibrary lib) { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException { logEnter("mmapGetPointer", "%s", mmap); - return lib.mmapGetPointer(delegate, mmap); + try { + return lib.mmapGetPointer(delegate, mmap); + } catch (UnsupportedPosixFeatureException e) { + throw logException("mmapGetPointer", e); + } } @ExportMessage @@ -1372,10 +1454,12 @@ final Object gethostname(@CachedLibrary("this.delegate") PosixSupportLibrary lib @ExportMessage final Object[] getnameinfo(UniversalSockAddr addr, int flags, - @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws GetAddrInfoException { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException, GetAddrInfoException { logEnter("getnameinfo", "%s, %d", addr, flags); try { return logExit("getnameinfo", "%s", lib.getnameinfo(delegate, addr, flags)); + } catch (UnsupportedPosixFeatureException e) { + throw logException("getnameinfo", e); } catch (GetAddrInfoException e) { throw logException("getnameinfo", e); } @@ -1383,10 +1467,12 @@ final Object[] getnameinfo(UniversalSockAddr addr, int flags, @ExportMessage final AddrInfoCursor getaddrinfo(Object node, Object service, int family, int sockType, int protocol, int flags, - @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws GetAddrInfoException { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException, GetAddrInfoException { logEnter("getaddrinfo", "%s, %s, %d, %d, %d, %d", node, service, family, sockType, protocol, flags); try { return logExit("getaddrinfo", "%s", lib.getaddrinfo(delegate, node, service, family, sockType, protocol, flags)); + } catch (UnsupportedPosixFeatureException e) { + throw logException("getaddrinfo", e); } catch (GetAddrInfoException e) { throw logException("getaddrinfo", e); } @@ -1507,10 +1593,12 @@ final UniversalSockAddr createUniversalSockAddrInet6(Inet6SockAddr src, @ExportMessage final UniversalSockAddr createUniversalSockAddrUnix(UnixSockAddr src, - @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws InvalidUnixSocketPathException { + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws UnsupportedPosixFeatureException, InvalidUnixSocketPathException { logEnter("createUniversalSockAddrUnix", "%s", src); try { return logExit("createUniversalSockAddrUnix", "%s", lib.createUniversalSockAddrUnix(delegate, src)); + } catch (UnsupportedPosixFeatureException e) { + throw logException("createUniversalSockAddrUnix", e); } catch (InvalidUnixSocketPathException e) { throw logException("createUniversalSockAddrUnix", e); } @@ -1570,10 +1658,22 @@ private static T logExit(String msg, String argFmt, T retVal) { return logExit(DEFAULT_LEVEL, msg, argFmt, retVal); } + @TruffleBoundary + private static UnsupportedPosixFeatureException logException(Level level, String msg, UnsupportedPosixFeatureException e) throws UnsupportedPosixFeatureException { + if (LOGGER.isLoggable(level)) { + LOGGER.log(level, msg + String.format(" -> throw unsupported, msg=%s", fixLogArg(e.getMessage()))); + } + throw e; + } + @TruffleBoundary private static PosixException logException(Level level, String msg, PosixException e) throws PosixException { if (LOGGER.isLoggable(level)) { - LOGGER.log(level, msg + String.format(" -> throw errno=%d, msg=%s", fixLogArgs(e.getErrorCode(), e.getMessage()))); + if (e instanceof PosixErrnoException errnoException) { + LOGGER.log(level, msg + String.format(" -> throw errno=%d, msg=%s", fixLogArgs(errnoException.getErrorCode(), errnoException.getMessage()))); + } else { + LOGGER.log(level, msg + String.format(" -> throw unsupported, msg=%s", fixLogArg(e.getMessage()))); + } } throw e; } @@ -1606,6 +1706,10 @@ private static PosixException logException(String msg, PosixException e) throws throw logException(DEFAULT_LEVEL, msg, e); } + private static UnsupportedPosixFeatureException logException(String msg, UnsupportedPosixFeatureException e) throws UnsupportedPosixFeatureException { + throw logException(DEFAULT_LEVEL, msg, e); + } + private static GetAddrInfoException logException(String msg, GetAddrInfoException e) throws GetAddrInfoException { throw logException(DEFAULT_LEVEL, msg, e); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIBz2Support.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIBz2Support.java deleted file mode 100644 index eda48b05e1..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIBz2Support.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * 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 - * - * 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; - -import com.oracle.graal.python.PythonLanguage; -import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.ThreadLocalAction.Access; -import com.oracle.truffle.api.TruffleLogger; - -/*- - * Generated using: - * scripts/nfi_gen.py -name Bz2 -cpath graalpython/com.oracle.graal.python.cext/bz2/bz2.c -lib libbz2support - */ -public class NFIBz2Support { - - private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NFIBz2Support.class); - - enum Bz2NativeFunctions implements NativeLibrary.NativeFunction { - - /*- - nfi_function: name('createStream') map('bzst_stream*', 'POINTER') - bzst_stream *bz_create_bzst_stream() - */ - bz_create_bzst_stream("(): POINTER"), - - /*- - nfi_function: name('getTimeElapsed') map('bzst_stream*', 'POINTER') static(true) - double bz_get_timeElapsed(bzst_stream* zst) - */ - bz_get_timeElapsed("(POINTER): DOUBLE"), - - /*- - nfi_function: name('deallocateStream') map('bzst_stream*', 'POINTER') - void bz_free_stream(bzst_stream* bzst) - */ - bz_free_stream("(POINTER): VOID"), - - /*- - nfi_function: name('gcReleaseHelper') map('bzst_stream*', 'POINTER') release(true) - void bz_gc_helper(bzst_stream* bzst) - */ - bz_gc_helper("(POINTER): VOID"), - - /*- - nfi_function: name('getNextInIndex') map('bzst_stream*', 'POINTER') - ssize_t bz_get_next_in_index(bzst_stream *bzst) - */ - bz_get_next_in_index("(POINTER): SINT64"), - - /*- - nfi_function: name('getBzsAvailInReal') map('bzst_stream*', 'POINTER') - ssize_t bz_get_bzs_avail_in_real(bzst_stream *bzst) - */ - bz_get_bzs_avail_in_real("(POINTER): SINT64"), - - /*- - nfi_function: name('setBzsAvailInReal') map('bzst_stream*', 'POINTER') - void bz_set_bzs_avail_in_real(bzst_stream *bzst, ssize_t v) - */ - bz_set_bzs_avail_in_real("(POINTER, SINT64): VOID"), - - /*- - nfi_function: name('getOutputBufferSize') map('bzst_stream*', 'POINTER') - size_t bz_get_output_buffer_size(bzst_stream *bzst) - */ - bz_get_output_buffer_size("(POINTER): UINT64"), - - /*- - nfi_function: name('getOutputBuffer') map('bzst_stream*', 'POINTER') - void bz_get_output_buffer(bzst_stream *bzst, Byte *dest) - */ - bz_get_output_buffer("(POINTER, [UINT8]): VOID"), - - /*- - nfi_function: name('compressInit') map('bzst_stream*', 'POINTER') - int bz_compressor_init(bzst_stream *bzst, int compresslevel) - */ - bz_compressor_init("(POINTER, SINT32): SINT32"), - - /*- - nfi_function: name('compress') map('bzst_stream*', 'POINTER') - int bz_compress(bzst_stream *bzst, Byte *data, ssize_t len, int action, ssize_t bufsize) - */ - bz_compress("(POINTER, [UINT8], SINT64, SINT32, SINT64): SINT32"), - - /*- - nfi_function: name('decompressInit') map('bzst_stream*', 'POINTER') - int bz_decompress_init(bzst_stream *bzst) - */ - bz_decompress_init("(POINTER): SINT32"), - - /*- - nfi_function: name('decompress') map('bzst_stream*', 'POINTER') - int bz_decompress(bzst_stream *bzst, Byte *input_buffer, ssize_t offset,ssize_t max_length,ssize_t bufsize, ssize_t bzs_avail_in_real) - */ - bz_decompress("(POINTER, [UINT8], SINT64, SINT64, SINT64, SINT64): SINT32"); - - private final String signature; - - Bz2NativeFunctions(String signature) { - this.signature = signature; - } - - @Override - public String signature() { - return signature; - } - } - - private static final String SUPPORTING_NATIVE_LIB_NAME = "bz2support"; - - private final PythonContext pythonContext; - private final NativeLibrary.TypedNativeLibrary typedNativeLib; - - @CompilerDirectives.CompilationFinal private boolean available; - - private NFIBz2Support(PythonContext context, NativeLibrary.NFIBackend backend, String noNativeAccessHelp) { - if (context.useNativeCompressionModules()) { - this.pythonContext = context; - this.typedNativeLib = NativeLibrary.create(PythonContext.getSupportLibName(SUPPORTING_NATIVE_LIB_NAME), Bz2NativeFunctions.values(), - backend, noNativeAccessHelp, false); - this.available = true; - } else { - this.pythonContext = null; - this.typedNativeLib = null; - this.available = false; - } - } - - public static NFIBz2Support createNative(PythonContext context, String noNativeAccessHelp) { - return new NFIBz2Support(context, NativeLibrary.NFIBackend.NATIVE, noNativeAccessHelp); - } - - public static NFIBz2Support createLLVM(PythonContext context, String noNativeAccessHelp) { - return new NFIBz2Support(context, NativeLibrary.NFIBackend.LLVM, noNativeAccessHelp); - } - - static class PointerReleaseCallback implements AsyncHandler.AsyncAction { - private final Pointer pointer; - - public PointerReleaseCallback(Pointer pointer) { - this.pointer = pointer; - } - - @Override - public void execute(PythonContext context, Access access) { - synchronized (pointer) { - if (pointer.isReleased()) { - return; - } - try { - pointer.doRelease(); - pointer.markReleased(); - LOGGER.finest("NFIBz2Support pointer has been freed"); - } catch (Exception e) { - LOGGER.severe("Error while trying to free NFIBz2Support pointer: " + e.getMessage()); - } - } - } - } - - public static class Pointer extends AsyncHandler.SharedFinalizer.FinalizableReference { - - private final NFIBz2Support lib; - - public Pointer(Object referent, Object ptr, NFIBz2Support lib) { - super(referent, ptr, lib.pythonContext.getSharedFinalizer()); - this.lib = lib; - } - - protected void doRelease() { - lib.gcReleaseHelper(getReference()); - } - - @Override - public AsyncHandler.AsyncAction release() { - if (!isReleased()) { - return new PointerReleaseCallback(this); - } - return null; - } - } - - public void notAvailable() { - if (available) { - CompilerAsserts.neverPartOfCompilation("Checking NFIBz2Support availability should only be done during initialization."); - available = false; - } - } - - public boolean isAvailable() { - return available; - } - - /** - * - * @param zst bzst_stream* zst - * @return double - */ - public Object getTimeElapsed(Object zst) { - return typedNativeLib.callUncached(pythonContext, Bz2NativeFunctions.bz_get_timeElapsed, zst); - } - - /** - * - * @param bzst bzst_stream* bzst - * - */ - public Object gcReleaseHelper(Object bzst) { - return typedNativeLib.callUncached(pythonContext, Bz2NativeFunctions.bz_gc_helper, bzst); - } - - /** - * - * - * @return bzst_stream* - */ - public Object createStream( - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.call(typedNativeLib, Bz2NativeFunctions.bz_create_bzst_stream); - } - - /** - * - * @param bzst bzst_stream* bzst - * - */ - public void deallocateStream(Object bzst, - NativeLibrary.InvokeNativeFunction invokeNode) { - invokeNode.call(typedNativeLib, Bz2NativeFunctions.bz_free_stream, bzst); - } - - /** - * - * @param bzst bzst_stream *bzst - * @return ssize_t - */ - public long getNextInIndex(Object bzst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, Bz2NativeFunctions.bz_get_next_in_index, bzst); - } - - /** - * - * @param bzst bzst_stream *bzst - * @return ssize_t - */ - public long getBzsAvailInReal(Object bzst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, Bz2NativeFunctions.bz_get_bzs_avail_in_real, bzst); - } - - /** - * - * @param bzst bzst_stream *bzst - * @param v ssize_t v - * - */ - public void setBzsAvailInReal(Object bzst, long v, - NativeLibrary.InvokeNativeFunction invokeNode) { - invokeNode.call(typedNativeLib, Bz2NativeFunctions.bz_set_bzs_avail_in_real, bzst, v); - } - - /** - * - * @param bzst bzst_stream *bzst - * @return size_t - */ - public long getOutputBufferSize(Object bzst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, Bz2NativeFunctions.bz_get_output_buffer_size, bzst); - } - - /** - * - * @param bzst bzst_stream *bzst - * @param dest Byte *dest - * - */ - public void getOutputBuffer(Object bzst, byte[] dest, - NativeLibrary.InvokeNativeFunction invokeNode) { - invokeNode.call(typedNativeLib, Bz2NativeFunctions.bz_get_output_buffer, bzst, dest); - } - - /** - * - * @param bzst bzst_stream *bzst - * @param compresslevel int compresslevel - * @return int - */ - public int compressInit(Object bzst, int compresslevel, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, Bz2NativeFunctions.bz_compressor_init, bzst, compresslevel); - } - - /** - * - * @param bzst bzst_stream *bzst - * @param data Byte *data - * @param len ssize_t len - * @param action int action - * @param bufsize ssize_t bufsize - * @return int - */ - public int compress(Object bzst, byte[] data, long len, int action, long bufsize, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, Bz2NativeFunctions.bz_compress, bzst, data, len, action, bufsize); - } - - /** - * - * @param bzst bzst_stream *bzst - * @return int - */ - public int decompressInit(Object bzst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, Bz2NativeFunctions.bz_decompress_init, bzst); - } - - /** - * - * @param bzst bzst_stream *bzst - * @param input_buffer Byte *input_buffer - * @param offset ssize_t offset - * @param max_length ssize_t max_length - * @param bufsize ssize_t bufsize - * @param bzs_avail_in_real ssize_t bzs_avail_in_real - * @return int - */ - public int decompress(Object bzst, byte[] input_buffer, long offset, long max_length, long bufsize, long bzs_avail_in_real, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, Bz2NativeFunctions.bz_decompress, bzst, input_buffer, offset, max_length, bufsize, bzs_avail_in_real); - } - -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFILZMASupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFILZMASupport.java deleted file mode 100644 index 8382844ca4..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFILZMASupport.java +++ /dev/null @@ -1,696 +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.runtime; - -import com.oracle.graal.python.PythonLanguage; -import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.ThreadLocalAction.Access; -import com.oracle.truffle.api.TruffleLogger; - -/*- - * Generated using: - * scripts/nfi_gen.py -name LZMA -cpath graalpython/com.oracle.graal.python.cext/lzma/lzma.c -lib liblzmasupport - */ -public class NFILZMASupport { - - private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NFILZMASupport.class); - - public static final int FORMAT_AUTO_INDEX = 0; - public static final int FORMAT_XZ_INDEX = 1; - public static final int FORMAT_ALONE_INDEX = 2; - public static final int FORMAT_RAW_INDEX = 3; - public static final int CHECK_NONE_INDEX = 0; - public static final int CHECK_CRC32_INDEX = 1; - public static final int CHECK_CRC64_INDEX = 2; - public static final int CHECK_SHA256_INDEX = 3; - public static final int CHECK_ID_MAX_INDEX = 4; - public static final int CHECK_UNKNOWN_INDEX = 5; - public static final int FILTER_LZMA1_INDEX = 0; - public static final int FILTER_LZMA2_INDEX = 1; - public static final int FILTER_DELTA_INDEX = 2; - public static final int FILTER_X86_INDEX = 3; - public static final int FILTER_POWERPC_INDEX = 4; - public static final int FILTER_IA64_INDEX = 5; - public static final int FILTER_ARM_INDEX = 6; - public static final int FILTER_ARMTHUMB_INDEX = 7; - public static final int FILTER_SPARC_INDEX = 8; - public static final int MF_HC3_INDEX = 0; - public static final int MF_HC4_INDEX = 1; - public static final int MF_BT2_INDEX = 2; - public static final int MF_BT3_INDEX = 3; - public static final int MF_BT4_INDEX = 4; - public static final int MODE_FAST_INDEX = 0; - public static final int MODE_NORMAL_INDEX = 1; - public static final int PRESET_DEFAULT_INDEX = 0; - public static final int PRESET_EXTREME_INDEX = 1; - public static final int ID_INDEX = 0; - public static final int PRESET_INDEX = 1; - public static final int DICT_SIZE_INDEX = 2; - public static final int LC_INDEX = 3; - public static final int LP_INDEX = 4; - public static final int PB_INDEX = 5; - public static final int MODE_INDEX = 6; - public static final int NICE_LEN_INDEX = 7; - public static final int MF_INDEX = 8; - public static final int DEPTH_INDEX = 9; - public static final int DIST_INDEX = 1; - public static final int START_OFFSET_INDEX = 1; - public static final int MAX_OPTS_INDEX = 10; - public static final int LZMA_ID_ERROR = 98; - public static final int LZMA_PRESET_ERROR = 99; - - enum LZMANativeFunctions implements NativeLibrary.NativeFunction { - - /*- - nfi_function: name('getMarcos') static(true) - void get_macros(int* formats, int* checks, uint64_t* filters, int* mfs, int* modes, uint64_t* preset) - */ - get_macros("([SINT32], [SINT32], [UINT64], [SINT32], [SINT32], [UINT64]): VOID"), - - /*- - nfi_function: name('createStream') map('lzmast_stream*', 'POINTER') - lzmast_stream *lzma_create_lzmast_stream() - */ - lzma_create_lzmast_stream("(): POINTER"), - - /*- - nfi_function: name('getTimeElapsed') map('lzmast_stream*', 'POINTER') static(true) - double lzma_get_timeElapsed(lzmast_stream* lzmast) - */ - lzma_get_timeElapsed("(POINTER): DOUBLE"), - - /*- - nfi_function: name('deallocateStream') map('lzmast_stream*', 'POINTER') - void lzma_free_stream(lzmast_stream* lzmast) - */ - lzma_free_stream("(POINTER): VOID"), - - /*- - nfi_function: name('gcReleaseHelper') map('lzmast_stream*', 'POINTER') release(true) - void lzma_gc_helper(lzmast_stream* lzmast) - */ - lzma_gc_helper("(POINTER): VOID"), - - /*- - nfi_function: name('getNextInIndex') map('lzmast_stream*', 'POINTER') - ssize_t lzma_get_next_in_index(lzmast_stream *lzmast) - */ - lzma_get_next_in_index("(POINTER): SINT64"), - - /*- - nfi_function: name('getLzsAvailIn') map('lzmast_stream*', 'POINTER') - size_t lzma_get_lzs_avail_in(lzmast_stream *lzmast) - */ - lzma_get_lzs_avail_in("(POINTER): UINT64"), - - /*- - nfi_function: name('getLzsAvailOut') map('lzmast_stream*', 'POINTER') - size_t lzma_get_lzs_avail_out(lzmast_stream *lzmast) - */ - lzma_get_lzs_avail_out("(POINTER): UINT64"), - - /*- - nfi_function: name('getLzsCheck') map('lzmast_stream*', 'POINTER') - int lzma_lzma_get_check(lzmast_stream *lzmast) - */ - lzma_lzma_get_check("(POINTER): SINT32"), - - /*- - nfi_function: name('setLzsAvailIn') map('lzmast_stream*', 'POINTER') - void lzma_set_lzs_avail_in(lzmast_stream *lzmast, size_t v) - */ - lzma_set_lzs_avail_in("(POINTER, UINT64): VOID"), - - /*- - nfi_function: name('getOutputBufferSize') map('lzmast_stream*', 'POINTER') - size_t lzma_get_output_buffer_size(lzmast_stream *lzmast) - */ - lzma_get_output_buffer_size("(POINTER): UINT64"), - - /*- - nfi_function: name('getOutputBuffer') map('lzmast_stream*', 'POINTER') - void lzma_get_output_buffer(lzmast_stream *lzmast, Byte *dest) - */ - lzma_get_output_buffer("(POINTER, [UINT8]): VOID"), - - /*- - nfi_function: name('checkIsSupported') - int lzma_lzma_check_is_supported(int check_id) - */ - lzma_lzma_check_is_supported("(SINT32): SINT32"), - - /*- - nfi_function: name('setFilterSpecLZMA') map('lzmast_stream*', 'POINTER') - int lzma_set_filter_spec_lzma(lzmast_stream *lzmast, int fidx, int64_t* opts) - */ - lzma_set_filter_spec_lzma("(POINTER, SINT32, [SINT64]): SINT32"), - - /*- - nfi_function: name('setFilterSpecDelta') map('lzmast_stream*', 'POINTER') - int lzma_set_filter_spec_delta(lzmast_stream *lzmast, int fidx, int64_t* opts) - */ - lzma_set_filter_spec_delta("(POINTER, SINT32, [SINT64]): SINT32"), - - /*- - nfi_function: name('setFilterSpecBCJ') map('lzmast_stream*', 'POINTER') - int lzma_set_filter_spec_bcj(lzmast_stream *lzmast, int fidx, int64_t* opts) - */ - lzma_set_filter_spec_bcj("(POINTER, SINT32, [SINT64]): SINT32"), - - /*- - nfi_function: name('encodeFilter') map('lzmast_stream*', 'POINTER') - int lzma_encode_filter_spec(lzmast_stream *lzmast, int64_t* opts) - */ - lzma_encode_filter_spec("(POINTER, [SINT64]): SINT32"), - - /*- - nfi_function: name('decodeFilter') map('lzmast_stream*', 'POINTER') - int lzma_decode_filter_spec(int64_t filter_id, Byte* encoded_props, int len, int64_t *opts) - */ - lzma_decode_filter_spec("(SINT64, [UINT8], SINT32, [SINT64]): SINT32"), - - /*- - nfi_function: name('lzmaEasyEncoder') map('lzmast_stream*', 'POINTER') - int lzma_lzma_easy_encoder(lzmast_stream *lzmast, uint32_t preset, int check) - */ - lzma_lzma_easy_encoder("(POINTER, UINT32, SINT32): SINT32"), - - /*- - nfi_function: name('lzmaStreamEncoder') map('lzmast_stream*', 'POINTER') - int lzma_lzma_stream_encoder(lzmast_stream *lzmast, int check) - */ - lzma_lzma_stream_encoder("(POINTER, SINT32): SINT32"), - - /*- - nfi_function: name('lzmaAloneEncoderPreset') map('lzmast_stream*', 'POINTER') - int lzma_lzma_alone_encoder_preset(lzmast_stream *lzmast, uint32_t preset) - */ - lzma_lzma_alone_encoder_preset("(POINTER, UINT32): SINT32"), - - /*- - nfi_function: name('lzmaAloneEncoder') map('lzmast_stream*', 'POINTER') - int lzma_lzma_alone_encoder(lzmast_stream *lzmast) - */ - lzma_lzma_alone_encoder("(POINTER): SINT32"), - - /*- - nfi_function: name('lzmaRawEncoder') map('lzmast_stream*', 'POINTER') - int lzma_lzma_raw_encoder(lzmast_stream *lzmast) - */ - lzma_lzma_raw_encoder("(POINTER): SINT32"), - - /*- - nfi_function: name('compress') map('lzmast_stream*', 'POINTER') - int lzma_compress(lzmast_stream *lzmast, Byte *data, size_t len, int iaction, ssize_t bufsize) - */ - lzma_compress("(POINTER, [UINT8], UINT64, SINT32, SINT64): SINT32"), - - /*- - nfi_function: name('lzmaRawDecoder') map('lzmast_stream*', 'POINTER') - int lzma_lzma_raw_decoder(lzmast_stream *lzmast) - */ - lzma_lzma_raw_decoder("(POINTER): SINT32"), - - /*- - nfi_function: name('lzmaAutoDecoder') map('lzmast_stream*', 'POINTER') - int lzma_lzma_auto_decoder(lzmast_stream *lzmast, uint64_t memlimit, uint32_t decoder_flags) - */ - lzma_lzma_auto_decoder("(POINTER, UINT64, UINT32): SINT32"), - - /*- - nfi_function: name('lzmaStreamDecoder') map('lzmast_stream*', 'POINTER') - int lzma_lzma_stream_decoder(lzmast_stream *lzmast, uint64_t memlimit, uint32_t decoder_flags) - */ - lzma_lzma_stream_decoder("(POINTER, UINT64, UINT32): SINT32"), - - /*- - nfi_function: name('lzmaAloneDecoder') map('lzmast_stream*', 'POINTER') - int lzma_lzma_alone_decoder(lzmast_stream *lzmast, uint64_t memlimit) - */ - lzma_lzma_alone_decoder("(POINTER, UINT64): SINT32"), - - /*- - nfi_function: name('decompress') map('lzmast_stream*', 'POINTER') - int lzma_decompress(lzmast_stream *lzmast, Byte *input_buffer, ssize_t offset,ssize_t max_length,ssize_t bufsize, size_t lzs_avail_in) - */ - lzma_decompress("(POINTER, [UINT8], SINT64, SINT64, SINT64, UINT64): SINT32"); - - private final String signature; - - LZMANativeFunctions(String signature) { - this.signature = signature; - } - - @Override - public String signature() { - return signature; - } - } - - private static final String SUPPORTING_NATIVE_LIB_NAME = "lzmasupport"; - - private final PythonContext pythonContext; - private final NativeLibrary.TypedNativeLibrary typedNativeLib; - - @CompilerDirectives.CompilationFinal private boolean available; - - private NFILZMASupport(PythonContext context, NativeLibrary.NFIBackend backend, String noNativeAccessHelp) { - if (context.useNativeCompressionModules()) { - this.pythonContext = context; - this.typedNativeLib = NativeLibrary.create(PythonContext.getSupportLibName(SUPPORTING_NATIVE_LIB_NAME), LZMANativeFunctions.values(), - backend, noNativeAccessHelp, true); - this.available = true; - } else { - this.pythonContext = null; - this.typedNativeLib = null; - this.available = false; - } - } - - public static NFILZMASupport createNative(PythonContext context, String noNativeAccessHelp) { - return new NFILZMASupport(context, NativeLibrary.NFIBackend.NATIVE, noNativeAccessHelp); - } - - public static NFILZMASupport createLLVM(PythonContext context, String noNativeAccessHelp) { - return new NFILZMASupport(context, NativeLibrary.NFIBackend.LLVM, noNativeAccessHelp); - } - - static class PointerReleaseCallback implements AsyncHandler.AsyncAction { - private final Pointer pointer; - - public PointerReleaseCallback(Pointer pointer) { - this.pointer = pointer; - } - - @Override - public void execute(PythonContext context, Access access) { - synchronized (pointer) { - if (pointer.isReleased()) { - return; - } - try { - pointer.doRelease(); - pointer.markReleased(); - LOGGER.finest("NFILZMASupport pointer has been freed"); - } catch (Exception e) { - LOGGER.severe("Error while trying to free NFILZMASupport pointer: " + e.getMessage()); - } - } - } - } - - public static class Pointer extends AsyncHandler.SharedFinalizer.FinalizableReference { - - private final NFILZMASupport lib; - - public Pointer(Object referent, Object ptr, NFILZMASupport lib) { - super(referent, ptr, lib.pythonContext.getSharedFinalizer()); - this.lib = lib; - } - - protected void doRelease() { - lib.gcReleaseHelper(getReference()); - } - - @Override - public AsyncHandler.AsyncAction release() { - if (!isReleased()) { - return new PointerReleaseCallback(this); - } - return null; - } - } - - public void notAvailable() { - if (available) { - CompilerAsserts.neverPartOfCompilation("Checking NFILZMASupport availability should only be done during initialization."); - available = false; - } - } - - public boolean isAvailable() { - return available; - } - - /** - * - * @param formats int* formats - * @param checks int* checks - * @param filters uint64_t* filters - * @param mfs int* mfs - * @param modes int* modes - * @param preset uint64_t* preset - * - */ - public Object getMacros(int[] formats, int[] checks, long[] filters, int[] mfs, int[] modes, long[] preset) { - return typedNativeLib.callUncached(pythonContext, LZMANativeFunctions.get_macros, formats, checks, filters, mfs, modes, preset); - } - - /** - * - * @param lzmast lzmast_stream* lzmast - * @return double - */ - public Object getTimeElapsed(Object lzmast) { - return typedNativeLib.callUncached(pythonContext, LZMANativeFunctions.lzma_get_timeElapsed, lzmast); - } - - /** - * - * @param lzmast lzmast_stream* lzmast - * - */ - public Object gcReleaseHelper(Object lzmast) { - return typedNativeLib.callUncached(pythonContext, LZMANativeFunctions.lzma_gc_helper, lzmast); - } - - /** - * - * - * @return lzmast_stream* - */ - public Object createStream( - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.call(typedNativeLib, LZMANativeFunctions.lzma_create_lzmast_stream); - } - - /** - * - * @param lzmast lzmast_stream* lzmast - * - */ - public void deallocateStream(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - invokeNode.call(typedNativeLib, LZMANativeFunctions.lzma_free_stream, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @return ssize_t - */ - public long getNextInIndex(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, LZMANativeFunctions.lzma_get_next_in_index, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @return size_t - */ - public long getLzsAvailIn(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, LZMANativeFunctions.lzma_get_lzs_avail_in, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @return size_t - */ - public long getLzsAvailOut(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, LZMANativeFunctions.lzma_get_lzs_avail_out, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @return int - */ - public int getLzsCheck(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_get_check, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param v size_t v - * - */ - public void setLzsAvailIn(Object lzmast, long v, - NativeLibrary.InvokeNativeFunction invokeNode) { - invokeNode.call(typedNativeLib, LZMANativeFunctions.lzma_set_lzs_avail_in, lzmast, v); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @return size_t - */ - public long getOutputBufferSize(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, LZMANativeFunctions.lzma_get_output_buffer_size, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param dest Byte *dest - * - */ - public void getOutputBuffer(Object lzmast, byte[] dest, - NativeLibrary.InvokeNativeFunction invokeNode) { - invokeNode.call(typedNativeLib, LZMANativeFunctions.lzma_get_output_buffer, lzmast, dest); - } - - /** - * - * @param check_id int check_id - * @return int - */ - public int checkIsSupported(int check_id, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_check_is_supported, check_id); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param fidx int fidx - * @param opts int64_t* opts - * @return int - */ - public int setFilterSpecLZMA(Object lzmast, int fidx, long[] opts, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_set_filter_spec_lzma, lzmast, fidx, opts); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param fidx int fidx - * @param opts int64_t* opts - * @return int - */ - public int setFilterSpecDelta(Object lzmast, int fidx, long[] opts, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_set_filter_spec_delta, lzmast, fidx, opts); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param fidx int fidx - * @param opts int64_t* opts - * @return int - */ - public int setFilterSpecBCJ(Object lzmast, int fidx, long[] opts, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_set_filter_spec_bcj, lzmast, fidx, opts); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param opts int64_t* opts - * @return int - */ - public int encodeFilter(Object lzmast, long[] opts, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_encode_filter_spec, lzmast, opts); - } - - /** - * - * @param filter_id int64_t filter_id - * @param encoded_props Byte* encoded_props - * @param len int len - * @param opts int64_t *opts - * @return int - */ - public int decodeFilter(long filter_id, byte[] encoded_props, int len, long[] opts, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_decode_filter_spec, filter_id, encoded_props, len, opts); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param preset uint32_t preset - * @param check int check - * @return int - */ - public int lzmaEasyEncoder(Object lzmast, long preset, int check, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_easy_encoder, lzmast, preset, check); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param check int check - * @return int - */ - public int lzmaStreamEncoder(Object lzmast, int check, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_stream_encoder, lzmast, check); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param preset uint32_t preset - * @return int - */ - public int lzmaAloneEncoderPreset(Object lzmast, long preset, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_alone_encoder_preset, lzmast, preset); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @return int - */ - public int lzmaAloneEncoder(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_alone_encoder, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @return int - */ - public int lzmaRawEncoder(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_raw_encoder, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param data Byte *data - * @param len size_t len - * @param iaction int iaction - * @param bufsize ssize_t bufsize - * @return int - */ - public int compress(Object lzmast, byte[] data, long len, int iaction, long bufsize, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_compress, lzmast, data, len, iaction, bufsize); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @return int - */ - public int lzmaRawDecoder(Object lzmast, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_raw_decoder, lzmast); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param memlimit uint64_t memlimit - * @param decoder_flags uint32_t decoder_flags - * @return int - */ - public int lzmaAutoDecoder(Object lzmast, long memlimit, long decoder_flags, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_auto_decoder, lzmast, memlimit, decoder_flags); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param memlimit uint64_t memlimit - * @param decoder_flags uint32_t decoder_flags - * @return int - */ - public int lzmaStreamDecoder(Object lzmast, long memlimit, long decoder_flags, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_stream_decoder, lzmast, memlimit, decoder_flags); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param memlimit uint64_t memlimit - * @return int - */ - public int lzmaAloneDecoder(Object lzmast, long memlimit, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_lzma_alone_decoder, lzmast, memlimit); - } - - /** - * - * @param lzmast lzmast_stream *lzmast - * @param input_buffer Byte *input_buffer - * @param offset ssize_t offset - * @param max_length ssize_t max_length - * @param bufsize ssize_t bufsize - * @param lzs_avail_in size_t lzs_avail_in - * @return int - */ - public int decompress(Object lzmast, byte[] input_buffer, long offset, long max_length, long bufsize, long lzs_avail_in, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, LZMANativeFunctions.lzma_decompress, lzmast, input_buffer, offset, max_length, bufsize, lzs_avail_in); - } - -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java deleted file mode 100644 index b1b36a4ec3..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java +++ /dev/null @@ -1,2756 +0,0 @@ -/* - * 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. - */ -// skip GIL -package com.oracle.graal.python.runtime; - -import static com.oracle.graal.python.nodes.StringLiterals.J_DEFAULT; -import static com.oracle.graal.python.nodes.StringLiterals.J_NATIVE; -import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE; -import static com.oracle.graal.python.nodes.StringLiterals.T_NATIVE; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_IN_ADDR_S_ADDR; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_IN; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_IN6; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_SA_FAMILY; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_STORAGE; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_UN_SUN_PATH; -import static com.oracle.graal.python.runtime.PosixConstants.AF_INET; -import static com.oracle.graal.python.runtime.PosixConstants.AF_INET6; -import static com.oracle.graal.python.runtime.PosixConstants.AF_UNIX; -import static com.oracle.graal.python.runtime.PosixConstants.AF_UNSPEC; -import static com.oracle.graal.python.runtime.PosixConstants.HOST_NAME_MAX; -import static com.oracle.graal.python.runtime.PosixConstants.INET6_ADDRSTRLEN; -import static com.oracle.graal.python.runtime.PosixConstants.INET_ADDRSTRLEN; -import static com.oracle.graal.python.runtime.PosixConstants.L_ctermid; -import static com.oracle.graal.python.runtime.PosixConstants.NI_MAXHOST; -import static com.oracle.graal.python.runtime.PosixConstants.NI_MAXSERV; -import static com.oracle.graal.python.runtime.PosixConstants.PATH_MAX; -import static com.oracle.graal.python.runtime.PosixConstants.WNOHANG; -import static com.oracle.graal.python.runtime.PosixConstants._POSIX_HOST_NAME_MAX; -import static com.oracle.graal.python.runtime.PosixSupportLibrary.POSIX_FILENAME_SEPARATOR; -import static com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; -import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR; -import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR_BE; -import static com.oracle.graal.python.util.PythonUtils.EMPTY_LONG_ARRAY; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; -import static com.oracle.graal.python.util.PythonUtils.callCallTarget; -import static com.oracle.truffle.api.CompilerDirectives.SLOWPATH_PROBABILITY; -import static com.oracle.truffle.api.CompilerDirectives.injectBranchProbability; -import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; -import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_8; - -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; - -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.objects.PNone; -import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; -import com.oracle.graal.python.nodes.ErrorMessages; -import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.runtime.PosixSupportLibrary.AcceptResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursor; -import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursorLibrary; -import com.oracle.graal.python.runtime.PosixSupportLibrary.Buffer; -import com.oracle.graal.python.runtime.PosixSupportLibrary.GetAddrInfoException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet4SockAddr; -import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet6SockAddr; -import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidUnixSocketPathException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.OpenPtyResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.RusageResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; -import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; -import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddrLibrary; -import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; -import com.oracle.graal.python.util.FunctionWithSignature; -import com.oracle.graal.python.util.OverflowException; -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.TruffleFile; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.TruffleLogger; -import com.oracle.truffle.api.TruffleSafepoint; -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.NeverDefault; -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.UnknownIdentifierException; -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.source.Source; -import com.oracle.truffle.api.strings.TruffleString; -import com.oracle.truffle.nfi.api.SignatureLibrary; - -import sun.misc.Unsafe; - -/** - * Implementation that invokes the native POSIX functions directly using NFI. This requires either - * that the native access is allowed or to configure managed LLVM backend for NFI. - */ -@ExportLibrary(PosixSupportLibrary.class) -public final class NFIPosixSupport extends PosixSupport { - private static final String SUPPORTING_NATIVE_LIB_NAME = "posix"; - - private static final int UNAME_BUF_LENGTH = 256; - private static final int DIRENT_NAME_BUF_LENGTH = 256; - private static final int PWD_OUTPUT_LEN = 5; - private static final int PWD_BUFFER_MAX_SIZE = Integer.MAX_VALUE >> 2; - - private static final int MAX_READ = Integer.MAX_VALUE / 2; - - private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NFIPosixSupport.class); - - private static final Unsafe UNSAFE = PythonUtils.initUnsafe(); - - private static final Object CRYPT_LOCK = new Object(); - - private enum PosixNativeFunction { - init_constants("([sint64], sint32):sint32"), - - get_errno("():sint32"), - set_errno("(sint32):void"), - call_mmap("(sint64, sint32, sint32, sint32, sint64):sint64"), - call_munmap("(sint64, sint64):sint32"), - call_msync("(sint64, sint64, sint64):void"), - call_strerror("(sint32, [sint8], sint32):void"), - call_getpid("():sint64"), - call_umask("(sint32):sint32"), - call_openat("(sint32, [sint8], sint32, sint32):sint32"), - call_close("(sint32):sint32"), - call_read("(sint32, [sint8], uint64):sint64"), - call_write("(sint32, [sint8], uint64):sint64"), - call_dup("(sint32):sint32"), - call_dup2("(sint32, sint32, sint32):sint32"), - call_pipe2("([sint32]):sint32"), - call_select("(sint32, [sint32], sint32, [sint32], sint32, [sint32], sint32, sint64, sint64, [sint8]):sint32"), - call_poll("(sint32, sint32, sint64, sint64):sint32"), - call_lseek("(sint32, sint64, sint32):sint64"), - call_ftruncate("(sint32, sint64):sint32"), - call_truncate("([sint8], sint64):sint32"), - call_fsync("(sint32):sint32"), - call_flock("(sint32, sint32):sint32"), - call_fcntl_lock("(sint32, sint32, sint32, sint32, sint64, sint64):sint32"), - call_fstatat("(sint32, [sint8], sint32, [sint64]):sint32"), - call_fstat("(sint32, [sint64]):sint32"), - call_statvfs("([sint8], [sint64]):sint32"), - call_fstatvfs("(sint32, [sint64]):sint32"), - call_uname("([sint8], [sint8], [sint8], [sint8], [sint8], sint32):sint32"), - call_unlinkat("(sint32, [sint8], sint32):sint32"), - call_linkat("(sint32, [sint8], sint32, [sint8], sint32):sint32"), - call_symlinkat("([sint8], sint32, [sint8]):sint32"), - call_mkdirat("(sint32, [sint8], sint32):sint32"), - call_getcwd("([sint8], uint64):sint32"), - call_chdir("([sint8]):sint32"), - call_fchdir("(sint32):sint32"), - call_isatty("(sint32):sint32"), - call_opendir("([sint8]):sint64"), - call_fdopendir("(sint32):sint64"), - call_closedir("(sint64):sint32"), - call_readdir("(sint64, [sint8], uint64, [sint64]):sint32"), - call_rewinddir("(sint64):void"), - call_utimensat("(sint32, [sint8], [sint64], sint32):sint32"), - call_futimens("(sint32, [sint64]):sint32"), - call_futimes("(sint32, [sint64]):sint32"), - call_lutimes("([sint8], [sint64]):sint32"), - call_utimes("([sint8], [sint64]):sint32"), - call_renameat("(sint32, [sint8], sint32, [sint8]):sint32"), - call_faccessat("(sint32, [sint8], sint32, sint32, sint32):sint32"), - call_fchmodat("(sint32, [sint8], sint32, sint32):sint32"), - call_fchmod("(sint32, sint32):sint32"), - call_fchownat("(sint32, [sint8], sint64, sint64, sint32):sint32"), - call_fchown("(sint32, sint64, sint64):sint32"), - call_readlinkat("(sint32, [sint8], [sint8], uint64):sint64"), - get_inheritable("(sint32):sint32"), - set_inheritable("(sint32, sint32):sint32"), - get_blocking("(sint32):sint32"), - set_blocking("(sint32, sint32):sint32"), - get_terminal_size("(sint32, [sint32]):sint32"), - call_kill("(sint64, sint32):sint32"), - call_killpg("(sint64, sint32):sint32"), - call_waitpid("(sint64, [sint32], sint32):sint64"), - call_wcoredump("(sint32):sint32"), - call_wifcontinued("(sint32):sint32"), - call_wifstopped("(sint32):sint32"), - call_wifsignaled("(sint32):sint32"), - call_wifexited("(sint32):sint32"), - call_wexitstatus("(sint32):sint32"), - call_wtermsig("(sint32):sint32"), - call_wstopsig("(sint32):sint32"), - call_getuid("():sint64"), - call_geteuid("():sint64"), - call_getgid("():sint64"), - call_getegid("():sint64"), - call_getppid("():sint64"), - call_getpgid("(sint64):sint64"), - call_setpgid("(sint64,sint64):sint32"), - call_getpgrp("():sint64"), - call_getsid("(sint64):sint64"), - call_setsid("():sint64"), - call_getgroups("(sint64, [sint64]):sint32"), - call_getrusage("(sint32, [sint64]):sint32"), - call_openpty("([sint32]):sint32"), - call_ctermid("([sint8]):sint32"), - call_setenv("([sint8], [sint8], sint32):sint32"), - call_unsetenv("([sint8]):sint32"), - fork_exec("([sint8], [sint64], sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, [sint32], sint64):sint32"), - call_execv("([sint8], [sint64], sint32):void"), - call_system("([sint8]):sint32"), - - call_getpwuid_r("(uint64,[sint8],sint32,[uint64]):sint32"), - call_getpwname_r("([sint8],[sint8],sint32,[uint64]):sint32"), - call_setpwent("():void"), - call_endpwent("():void"), - call_getpwent("([sint64]):pointer"), - get_getpwent_data("(pointer,[sint8],sint32,[uint64]):sint32"), - get_sysconf_getpw_r_size_max("():sint64"), - - call_socket("(sint32, sint32, sint32):sint32"), - call_accept("(sint32, [sint8], [sint32]):sint32"), - call_bind("(sint32, [sint8], sint32):sint32"), - call_connect("(sint32, [sint8], sint32):sint32"), - call_listen("(sint32, sint32):sint32"), - call_getpeername("(sint32, [sint8], [sint32]):sint32"), - call_getsockname("(sint32, [sint8], [sint32]):sint32"), - call_send("(sint32, [sint8], sint32, sint32, sint32):sint32"), - call_sendto("(sint32, [sint8], sint32, sint32, sint32, [sint8], sint32):sint32"), - call_recv("(sint32, [sint8], sint32, sint32, sint32):sint32"), - call_recvfrom("(sint32, [sint8], sint32, sint32, sint32, [sint8], [sint32]):sint32"), - call_shutdown("(sint32, sint32): sint32"), - call_getsockopt("(sint32, sint32, sint32, [sint8], [sint32]):sint32"), - call_setsockopt("(sint32, sint32, sint32, [sint8], sint32):sint32"), - - call_inet_addr("([sint8]):sint32"), - call_inet_aton("([sint8]):sint64"), - call_inet_ntoa("(sint32, [sint8]):sint32"), - call_inet_pton("(sint32, [sint8], [sint8]):sint32"), - call_inet_ntop("(sint32, [sint8], [sint8], sint32):sint32"), - call_gethostname("([sint8], sint64):sint32"), - - call_getnameinfo("([sint8], sint32, [sint8], sint32, [sint8], sint32, sint32):sint32"), - call_getaddrinfo("([sint8], [sint8], sint32, sint32, sint32, sint32, [sint64]):sint32"), - call_freeaddrinfo("(sint64):void"), - call_gai_strerror("(sint32, [sint8], sint32):void"), - get_addrinfo_members("(sint64, [sint32], [sint64], [sint8]):sint32"), - - call_sem_open("([sint8], sint32, sint32, sint32):pointer"), - call_sem_close("(pointer):sint32"), - call_sem_unlink("([sint8]):sint32"), - call_sem_getvalue("(pointer, [sint32]):sint32"), - call_sem_post("(pointer):sint32"), - call_sem_wait("(pointer):sint32"), - call_sem_trywait("(pointer):sint32"), - call_sem_timedwait("(pointer, sint64):sint32"), - - call_ioctl_bytes("(sint32, uint64, [sint8]):sint32"), - call_ioctl_int("(sint32, uint64, sint32):sint32"), - - call_sysconf("(sint32):sint64"), - - crypt("([sint8], [sint8]):sint64"); - - private final String signature; - - PosixNativeFunction(String signature) { - this.signature = signature; - } - } - - protected static final class InvokeNativeFunction extends Node { - private static final InvokeNativeFunction UNCACHED = new InvokeNativeFunction(SignatureLibrary.getUncached(), InteropLibrary.getUncached()); - - @Child private SignatureLibrary functionInterop; - @Child private InteropLibrary resultInterop; - - public InvokeNativeFunction(SignatureLibrary functionInterop, InteropLibrary resultInterop) { - this.functionInterop = functionInterop; - this.resultInterop = resultInterop; - } - - @NeverDefault - public static InvokeNativeFunction create() { - return new InvokeNativeFunction(SignatureLibrary.getFactory().createDispatched(2), null); - } - - public static InvokeNativeFunction getUncached() { - return UNCACHED; - } - - public Object call(NFIPosixSupport posix, PosixNativeFunction function, Object... args) { - if (injectBranchProbability(SLOWPATH_PROBABILITY, posix.nfiLibrary == null)) { - loadLibrary(this, posix); - } - if (injectBranchProbability(SLOWPATH_PROBABILITY, posix.cachedFunctions.get(function.ordinal()) == null)) { - loadFunction(this, posix, posix.nfiLibrary, function); - } - FunctionWithSignature funObject = posix.cachedFunctions.get(function.ordinal()); - try { - return functionInterop.call(funObject.signature(), funObject.function(), args); - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - public long callLong(NFIPosixSupport posix, PosixNativeFunction function, Object... args) { - try { - return getResultInterop().asLong(call(posix, function, args)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - public int callInt(NFIPosixSupport posix, PosixNativeFunction function, Object... args) { - try { - return getResultInterop().asInt(call(posix, function, args)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - public byte callByte(NFIPosixSupport posix, PosixNativeFunction function, Object... args) { - try { - return getResultInterop().asByte(call(posix, function, args)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - // Temporary - will be replaced with something else when we move this to Truffle - private static String getLibPath(PythonContext context) { - CompilerAsserts.neverPartOfCompilation(); - String libPythonName = PythonContext.getSupportLibName(NFIPosixSupport.SUPPORTING_NATIVE_LIB_NAME); - TruffleFile homePath = context.getEnv().getInternalTruffleFile(context.getCAPIHome().toJavaStringUncached()); - TruffleFile file = homePath.resolve(libPythonName); - return file.getPath(); - } - - @TruffleBoundary - private static void loadLibrary(Node node, NFIPosixSupport posix) { - String path = getLibPath(posix.context); - try { - posix.nfiLibrary = loadLibrary(node, posix, path); - } catch (Throwable e) { - throw new UnsupportedOperationException(String.format(""" - Could not load posix support library from path '%s'. Troubleshooting:\s - Check permissions of the file. - Missing runtime Maven dependency 'org.graalvm.truffle:truffle-nfi-libffi' (should be a dependency of `org.graalvm.polyglot:python{-community}`)?""", - path)); - } - } - - @TruffleBoundary - private static Object loadLibrary(Node node, NFIPosixSupport posix, String path) { - String backend = posix.nfiBackend.toJavaStringUncached(); - Env env = posix.context.getEnv(); - - posix.context.ensureNFILanguage(null, "PosixModuleBackend", "native"); - - Source loadSrc; - if (path != null) { - String withClause = backend.equals(J_NATIVE) ? "" : "with " + backend; - String src = String.format("%sload (RTLD_LOCAL) \"%s\"", withClause, path); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine(String.format("Loading native library: %s", src)); - } - loadSrc = Source.newBuilder(J_NFI_LANGUAGE, src, "load:" + SUPPORTING_NATIVE_LIB_NAME).internal(true).build(); - } else { - loadSrc = Source.newBuilder(J_NFI_LANGUAGE, J_DEFAULT, J_DEFAULT).internal(true).build(); - } - - return callCallTarget(env.parseInternal(loadSrc), node); - } - - @TruffleBoundary - private static void loadFunction(Node node, NFIPosixSupport posix, Object library, PosixNativeFunction function) { - Object unbound; - try { - InteropLibrary interop = InteropLibrary.getUncached(); - - String sig = String.format("with %s %s", posix.nfiBackend, function.signature); - Source sigSrc = Source.newBuilder(J_NFI_LANGUAGE, sig, "posix-nfi-signature").internal(true).build(); - Object signature = PythonUtils.callCallTarget(posix.context.getEnv().parseInternal(sigSrc), node); - unbound = interop.readMember(library, function.name()); - posix.cachedFunctions.set(function.ordinal(), new FunctionWithSignature(signature, unbound)); - } catch (UnsupportedMessageException | UnknownIdentifierException e) { - throw CompilerDirectives.shouldNotReachHere(function.name(), e); - } - } - - public InteropLibrary getResultInterop() { - if (resultInterop == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - resultInterop = insert(InteropLibrary.getFactory().createDispatched(2)); - } - return resultInterop; - } - } - - private final PythonContext context; - private final TruffleString nfiBackend; - private volatile Object nfiLibrary; - private volatile Object cryptLibrary; - private final AtomicReferenceArray cachedFunctions; - @CompilationFinal(dimensions = 1) private long[] constantValues; - - public NFIPosixSupport(PythonContext context, TruffleString nfiBackend) { - assert nfiBackend.equalsUncached(T_NATIVE, TS_ENCODING); - this.context = context; - this.nfiBackend = nfiBackend; - this.cachedFunctions = new AtomicReferenceArray<>(PosixNativeFunction.values().length); - setEnv(context.getEnv()); - } - - long getConstant(NFIPosixConstants constant) { - if (constantValues == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - constantValues = new long[NFIPosixConstants.values().length]; - int result = InvokeNativeFunction.getUncached().callInt(this, PosixNativeFunction.init_constants, constantValues, constantValues.length); - if (result != 0) { - throw CompilerDirectives.shouldNotReachHere("Mismatched build of posix native library"); - } - } - return constantValues[constant.ordinal()]; - } - - @Override - public void setEnv(Env env) { - if (env.isPreInitialization()) { - return; - } - // Java NIO (and TruffleFile) do not expect/support changing native working directory since - // it is inherently thread-unsafe operation. It is not defined how NIO behaves when native - // cwd changes, thus we need to prevent TruffleFile from resolving relative paths using - // NIO by setting Truffle cwd to a know value. This cannot be done lazily in chdir() because - // native cwd is global, but Truffle cwd is per context. - // TruffleFile will be unaware of the real working directory and keep resolving against the - // original working directory. This should not matter since we do not use TruffleFile for - // ordinary I/O when using NFI backend. - try { - TruffleFile truffleFile = context.getEnv().getInternalTruffleFile(".").getAbsoluteFile(); - context.getEnv().setCurrentWorkingDirectory(truffleFile); - } catch (Exception e) { - LOGGER.log(Level.WARNING, "Unable to change Truffle working directory", e); - } - } - - @ExportMessage - public TruffleString getBackend() { - return nfiBackend; - } - - @ExportMessage - public TruffleString strerror(int errorCode, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) { - // From man pages: The GNU C Library uses a buffer of 1024 characters for strerror(). - // This buffer size therefore should be sufficient to avoid an ERANGE error when calling - // strerror_r(). - byte[] buf = new byte[1024]; - invokeNode.call(this, PosixNativeFunction.call_strerror, errorCode, buf, buf.length); - // TODO PyUnicode_DecodeLocale - return cStringToTruffleString(buf, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - @ExportMessage - public long getpid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getpid); - } - - @ExportMessage - public int umask(int mask, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_umask, mask); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public int openat(int dirFd, Object pathname, int flags, int mode, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int fd = invokeNode.callInt(this, PosixNativeFunction.call_openat, dirFd, pathToCString(pathname), flags, mode); - if (fd < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return fd; - } - - @ExportMessage - public int close(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - final int rv = invokeNode.callInt(this, PosixNativeFunction.call_close, fd); - if (rv < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return rv; - } - - @ExportMessage - public Buffer read(int fd, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long count = Math.min(length, MAX_READ); - Buffer buffer = Buffer.allocate(count); - setErrno(invokeNode, 0); // TODO CPython does this, but do we need it? - long n = invokeNode.callLong(this, PosixNativeFunction.call_read, fd, buffer.data, count); - if (n < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return buffer.withLength(n); - } - - @ExportMessage - public long write(int fd, Buffer data, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - setErrno(invokeNode, 0); // TODO CPython does this, but do we need it? - long n = invokeNode.callLong(this, PosixNativeFunction.call_write, fd, data.data, data.length); - if (n < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return n; - } - - @ExportMessage - public int dup(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int newFd = invokeNode.callInt(this, PosixNativeFunction.call_dup, fd); - if (newFd < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return newFd; - } - - @ExportMessage - public int dup2(int fd, int fd2, boolean inheritable, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int newFd = invokeNode.callInt(this, PosixNativeFunction.call_dup2, fd, fd2, inheritable ? 1 : 0); - if (newFd < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return newFd; - } - - @ExportMessage - public boolean getInheritable(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.get_inheritable, fd); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result != 0; - } - - @ExportMessage - public void setInheritable(int fd, boolean inheritable, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if (invokeNode.callInt(this, PosixNativeFunction.set_inheritable, fd, inheritable ? 1 : 0) < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int[] pipe( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int[] fds = new int[2]; - if (invokeNode.callInt(this, PosixNativeFunction.call_pipe2, fds) != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return fds; - } - - @ExportMessage - public SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeval timeout, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int largestFD = findMax(readfds, -1); - largestFD = findMax(writefds, largestFD); - largestFD = findMax(errorfds, largestFD); - // This will be treated as boolean array (output parameter), each item indicating if given - // FD was selected or not - byte[] selected = new byte[readfds.length + writefds.length + errorfds.length]; - int nfds = largestFD == -1 ? 0 : largestFD + 1; - long secs = -1, usecs = -1; - if (timeout != null) { - secs = timeout.getSeconds(); - usecs = timeout.getMicroseconds(); - } - int result = invokeNode.callInt(this, PosixNativeFunction.call_select, nfds, - readfds, readfds.length, - writefds, writefds.length, - errorfds, errorfds.length, - secs, usecs, selected); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new SelectResult( - selectFillInResult(readfds, selected, 0), - selectFillInResult(writefds, selected, readfds.length), - selectFillInResult(errorfds, selected, readfds.length + writefds.length)); - - } - - private static boolean[] selectFillInResult(int[] fds, byte[] selected, int selectedOffset) { - boolean[] res = new boolean[fds.length]; - for (int i = 0; i < fds.length; i++) { - res[i] = selected[selectedOffset + i] != 0; - } - return res; - } - - private static int findMax(int[] items, int currentMax) { - int max = currentMax; - for (int item : items) { - if (item > max) { - max = item; - } - } - return max; - } - - @ExportMessage - public boolean poll(int fd, boolean forWriting, Timeval timeout, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long secs = -1, usecs = -1; - if (timeout != null) { - secs = timeout.getSeconds(); - usecs = timeout.getMicroseconds(); - } - int result = invokeNode.callInt(this, PosixNativeFunction.call_poll, fd, - forWriting ? 1 : 0, secs, usecs); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - if (result == 0) { - return false; - } else { - return true; - } - } - - @ExportMessage - public long lseek(int fd, long offset, int how, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long res = invokeNode.callLong(this, PosixNativeFunction.call_lseek, fd, offset, how); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public void ftruncate(int fd, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_ftruncate, fd, length); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void truncate(Object path, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_truncate, pathToCString(path), length); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void fsync(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_fsync, fd); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - void flock(int fd, int operation, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_flock, fd, operation); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - void fcntlLock(int fd, boolean blocking, int lockType, int whence, long start, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_fcntl_lock, fd, blocking, lockType, whence, start, length); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public boolean getBlocking(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.get_blocking, fd); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result != 0; - } - - @ExportMessage - public void setBlocking(int fd, boolean blocking, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if (invokeNode.callInt(this, PosixNativeFunction.set_blocking, fd, blocking ? 1 : 0) < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int[] getTerminalSize(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int[] size = new int[2]; - if (invokeNode.callInt(this, PosixNativeFunction.get_terminal_size, fd, size) != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return size; - } - - @ExportMessage - public long sysconf(int name, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long result = invokeNode.callLong(this, PosixNativeFunction.call_sysconf, name); - if (result == -1) { - int errno = getErrno(invokeNode); - if (errno != 0) { - throw newPosixException(invokeNode, errno); - } - } - return result; - } - - @ExportMessage - public long[] fstatat(int dirFd, Object pathname, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] out = new long[13]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_fstatat, dirFd, pathToCString(pathname), followSymlinks ? 1 : 0, out); - if (res != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return out; - } - - @ExportMessage - public long[] fstat(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] out = new long[13]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_fstat, fd, out); - if (res != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return out; - } - - @ExportMessage - public long[] statvfs(Object path, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] out = new long[11]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_statvfs, pathToCString(path), out); - if (res != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return out; - } - - @ExportMessage - public long[] fstatvfs(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] out = new long[11]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_fstatvfs, fd, out); - if (res != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return out; - } - - @ExportMessage - public Object[] uname( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - byte[] sys = new byte[UNAME_BUF_LENGTH]; - byte[] node = new byte[UNAME_BUF_LENGTH]; - byte[] rel = new byte[UNAME_BUF_LENGTH]; - byte[] ver = new byte[UNAME_BUF_LENGTH]; - byte[] machine = new byte[UNAME_BUF_LENGTH]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_uname, sys, node, rel, ver, machine, UNAME_BUF_LENGTH); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new Object[]{ - // TODO PyUnicode_DecodeFSDefault - cStringToTruffleString(sys, fromByteArrayNode, switchEncodingFromUtf8Node), - cStringToTruffleString(node, fromByteArrayNode, switchEncodingFromUtf8Node), - cStringToTruffleString(rel, fromByteArrayNode, switchEncodingFromUtf8Node), - cStringToTruffleString(ver, fromByteArrayNode, switchEncodingFromUtf8Node), - cStringToTruffleString(machine, fromByteArrayNode, switchEncodingFromUtf8Node) - }; - } - - @ExportMessage - public void unlinkat(int dirFd, Object pathname, boolean rmdir, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_unlinkat, dirFd, pathToCString(pathname), rmdir ? 1 : 0); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void linkat(int oldFdDir, Object oldPath, int newFdDir, Object newPath, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_linkat, oldFdDir, pathToCString(oldPath), newFdDir, pathToCString(newPath), flags); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void symlinkat(Object target, int linkpathDirFd, Object linkpath, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_symlinkat, pathToCString(target), linkpathDirFd, pathToCString(linkpath)); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void mkdirat(int dirFd, Object pathname, int mode, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_mkdirat, dirFd, pathToCString(pathname), mode); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public Object getcwd( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - for (int bufLen = 1024;; bufLen += 1024) { - Buffer buffer = Buffer.allocate(bufLen); - int n = invokeNode.callInt(this, PosixNativeFunction.call_getcwd, buffer.data, bufLen); - if (n == 0) { - buffer = buffer.withLength(findZero(buffer.data)); - return buffer; - } - int errno = getErrno(invokeNode); - if (errno != OSErrorEnum.ERANGE.getNumber()) { - throw newPosixException(invokeNode, errno); - } - } - } - - @ExportMessage - public void chdir(Object path, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_chdir, pathToCString(path)); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void fchdir(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_fchdir, fd); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public boolean isatty(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_isatty, fd) != 0; - } - - @ExportMessage - public Object opendir(Object path, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long ptr = invokeNode.callLong(this, PosixNativeFunction.call_opendir, pathToCString(path)); - if (ptr == 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return ptr; - } - - @ExportMessage - public Object fdopendir(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long ptr = invokeNode.callLong(this, PosixNativeFunction.call_fdopendir, fd); - if (ptr == 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return ptr; - } - - @ExportMessage - public void closedir(Object dirStreamObj, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_closedir, dirStreamObj); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public Object readdir(Object dirStreamObj, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - Buffer name = Buffer.allocate(DIRENT_NAME_BUF_LENGTH); - long[] out = new long[2]; - int result; - do { - result = invokeNode.callInt(this, PosixNativeFunction.call_readdir, dirStreamObj, name.data, DIRENT_NAME_BUF_LENGTH, out); - } while (result != 0 && name.data[0] == '.' && (name.data[1] == 0 || (name.data[1] == '.' && name.data[2] == 0))); - if (result != 0) { - return new DirEntry(name.withLength(findZero(name.data)), out[0], (int) out[1]); - } - int errno = getErrno(invokeNode); - if (errno == 0) { - return null; - } - throw newPosixException(invokeNode, errno); - } - - @ExportMessage - public void rewinddir(Object dirStreamObj, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - invokeNode.call(this, PosixNativeFunction.call_rewinddir, dirStreamObj); - } - - @ExportMessage - @SuppressWarnings("static-method") - public Object dirEntryGetName(Object dirEntryObj) { - DirEntry dirEntry = (DirEntry) dirEntryObj; - return dirEntry.name; - } - - @ExportMessage - public static class DirEntryGetPath { - @Specialization(guards = "endsWithSlash(scandirPath)") - static Buffer withSlash(@SuppressWarnings("unused") NFIPosixSupport receiver, DirEntry dirEntry, Object scandirPath) { - Buffer scandirPathBuffer = (Buffer) scandirPath; - int pathLen = scandirPathBuffer.data.length; - int nameLen = (int) dirEntry.name.length; - byte[] buf = new byte[pathLen + nameLen]; - PythonUtils.arraycopy(scandirPathBuffer.data, 0, buf, 0, pathLen); - PythonUtils.arraycopy(dirEntry.name.data, 0, buf, pathLen, nameLen); - return Buffer.wrap(buf); - } - - @Specialization(guards = "!endsWithSlash(scandirPath)") - static Buffer withoutSlash(@SuppressWarnings("unused") NFIPosixSupport receiver, DirEntry dirEntry, Object scandirPath) { - Buffer scandirPathBuffer = (Buffer) scandirPath; - int pathLen = scandirPathBuffer.data.length; - int nameLen = (int) dirEntry.name.length; - byte[] buf = new byte[pathLen + 1 + nameLen]; - PythonUtils.arraycopy(scandirPathBuffer.data, 0, buf, 0, pathLen); - buf[pathLen] = POSIX_FILENAME_SEPARATOR; - PythonUtils.arraycopy(dirEntry.name.data, 0, buf, pathLen + 1, nameLen); - return Buffer.wrap(buf); - } - - protected static boolean endsWithSlash(Object path) { - Buffer b = (Buffer) path; - return b.data[b.data.length - 1] == POSIX_FILENAME_SEPARATOR; - } - } - - @ExportMessage - @SuppressWarnings("static-method") - public long dirEntryGetInode(Object dirEntry) { - DirEntry entry = (DirEntry) dirEntry; - return entry.ino; - } - - @ExportMessage - @SuppressWarnings("static-method") - public int dirEntryGetType(Object dirEntryObj) { - DirEntry dirEntry = (DirEntry) dirEntryObj; - return dirEntry.type; - } - - @ExportMessage - public void utimensat(int dirFd, Object pathname, long[] timespec, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert PosixConstants.HAVE_UTIMENSAT.value; - assert timespec == null || timespec.length == 4; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_utimensat, dirFd, pathToCString(pathname), wrap(timespec), followSymlinks ? 1 : 0); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void futimens(int fd, long[] timespec, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert PosixConstants.HAVE_FUTIMENS.value; - assert timespec == null || timespec.length == 4; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_futimens, fd, wrap(timespec)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void futimes(int fd, Timeval[] timeval, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert timeval == null || timeval.length == 2; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_futimes, fd, wrap(timeval)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void lutimes(Object filename, Timeval[] timeval, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert timeval == null || timeval.length == 2; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_lutimes, pathToCString(filename), wrap(timeval)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void utimes(Object filename, Timeval[] timeval, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert timeval == null || timeval.length == 2; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_utimes, pathToCString(filename), wrap(timeval)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void renameat(int oldDirFd, Object oldPath, int newDirFd, Object newPath, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_renameat, oldDirFd, pathToCString(oldPath), newDirFd, pathToCString(newPath)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public boolean faccessat(int dirFd, Object path, int mode, boolean effectiveIds, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_faccessat, dirFd, pathToCString(path), mode, effectiveIds ? 1 : 0, followSymlinks ? 1 : 0); - if (ret != 0 && LOGGER.isLoggable(Level.FINE)) { - log(Level.FINE, "faccessat return value: %d, errno: %d", ret, getErrno(invokeNode)); - } - return ret == 0; - } - - @ExportMessage - public void fchmodat(int dirFd, Object path, int mode, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchmodat, dirFd, pathToCString(path), mode, followSymlinks ? 1 : 0); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void fchmod(int fd, int mode, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchmod, fd, mode); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchownat, dirFd, pathToCString(path), owner, group, followSymlinks ? 1 : 0); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void fchown(int fd, long owner, long group, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchown, fd, owner, group); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public Object readlinkat(int dirFd, Object path, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - Buffer buffer = Buffer.allocate(PATH_MAX.value); - long n = invokeNode.callLong(this, PosixNativeFunction.call_readlinkat, dirFd, pathToCString(path), buffer.data, PATH_MAX.value); - if (n < 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return buffer.withLength(n); - } - - @ExportMessage - public void kill(long pid, int signal, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_kill, pid, signal); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void killpg(long pgid, int signal, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_killpg, pgid, signal); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public long[] waitpid(long pid, int options, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int[] status = new int[1]; - boolean hasNohang = (options & WNOHANG.getValueIfDefined()) != 0; - int subOptions = options | WNOHANG.getValueIfDefined(); - Object wrappedStatus = status; - long res = invokeNode.callLong(this, PosixNativeFunction.call_waitpid, pid, wrappedStatus, subOptions); - while (res == 0 && !hasNohang) { - TruffleSafepoint.setBlockedThreadInterruptible(invokeNode, (ignored) -> { - Thread.sleep(20); - }, null); - res = invokeNode.callLong(this, PosixNativeFunction.call_waitpid, pid, wrappedStatus, subOptions); - } - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new long[]{res, status[0]}; - } - - @ExportMessage - public boolean wcoredump(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wcoredump, status) != 0; - } - - @ExportMessage - public boolean wifcontinued(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wifcontinued, status) != 0; - } - - @ExportMessage - public boolean wifstopped(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wifstopped, status) != 0; - } - - @ExportMessage - public boolean wifsignaled(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wifsignaled, status) != 0; - } - - @ExportMessage - public boolean wifexited(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wifexited, status) != 0; - } - - @ExportMessage - public int wexitstatus(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wexitstatus, status); - } - - @ExportMessage - public int wtermsig(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wtermsig, status); - } - - @ExportMessage - public int wstopsig(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wstopsig, status); - } - - @ExportMessage - public long getuid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getuid); - } - - @ExportMessage - public long geteuid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_geteuid); - } - - @ExportMessage - public long getgid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getgid); - } - - @ExportMessage - public long getegid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getegid); - } - - @ExportMessage - public long getppid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getppid); - } - - @ExportMessage - public void setpgid(long pid, long pgid, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_setpgid, pid, pgid); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public long getpgid(long pid, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long res = invokeNode.callLong(this, PosixNativeFunction.call_getpgid, pid); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public long getpgrp(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getpgrp); - } - - @ExportMessage - public long getsid(long pid, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long res = invokeNode.callLong(this, PosixNativeFunction.call_getsid, pid); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public long setsid( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long res = invokeNode.callLong(this, PosixNativeFunction.call_setsid); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public long[] getgroups( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - // The first call gets us the number of groups, so we can allocate the output array - int res = invokeNode.callInt(this, PosixNativeFunction.call_getgroups, 0, 0); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - if (res == 0) { - return EMPTY_LONG_ARRAY; - } - long[] groups = new long[res]; - res = invokeNode.callInt(this, PosixNativeFunction.call_getgroups, groups.length, groups); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return groups; - } - - @ExportMessage - public RusageResult getrusage(int who, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] result = new long[16]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getrusage, who, result); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new RusageResult(Double.longBitsToDouble(result[0]), Double.longBitsToDouble(result[1]), - result[2], result[3], result[4], result[5], - result[6], result[7], result[8], result[9], result[10], - result[11], result[12], result[13], result[14], result[15]); - } - - @ExportMessage - public OpenPtyResult openpty(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int[] outvars = new int[2]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_openpty, outvars); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new OpenPtyResult(outvars[0], outvars[1]); - } - - @ExportMessage - public TruffleString ctermid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - byte[] buf = new byte[L_ctermid.value]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_ctermid, buf); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - // TODO PyUnicode_DecodeFSDefault - return cStringToTruffleString(buf, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - @ExportMessage - public void setenv(Object name, Object value, boolean overwrite, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_setenv, pathToCString(name), pathToCString(value), overwrite ? 1 : 0); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void unsetenv(Object name, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_unsetenv, pathToCString(name)); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd, - int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - - // The following strings and string arrays need to be present in the native function: - // - char** of executable names ('\0'-terminated strings with an extra NULL at the end) - // - char** of arguments ('\0'-terminated strings with an extra NULL at the end) - // - an optional char** of env variables ('\0'-terminated strings with an extra NULL at the - // end), must distinguish between NULL (child inherits env) and an empty array (child gets - // empty env) - // - an optional char* cwd ('\0'-terminated string or NULL) - // We do this by concatenating all strings (including their terminating '\0' characters) - // into one large byte buffer (which becomes 'char *') and pass an additional array of - // offsets to mark where the individual strings begin. To prevent memory allocation - // in C (and related free()), we reuse this array of integer offsets as an array of - // C-strings (char **). For this reason, the array of offsets is allocated as long[]. - // In the offsets array we mark the places where NULL should be with a special value -1. - // All that is left is to let the native function know where in the offsets array the - // individual string arrays begin: - // - executable names are always at index 0 - // - argsPos is the index in the offsets array pointing to the first argument - // - envPos is either -1 or an index in the offsets array pointing to the first env string - // - cwdPos is either -1 or an index in the offsets array pointing to the cwd string - - // First we calculate the lengths of the offsets array and the string buffer (dataLen). - int offsetsLen; - int argsPos; - int envPos; - int cwdPos; - long dataLen; - - try { - offsetsLen = executables.length + 1; - dataLen = addLengthsOfCStrings(0, executables); - - argsPos = offsetsLen; - offsetsLen += args.length + 1; - dataLen = addLengthsOfCStrings(dataLen, args); - - if (env != null) { - envPos = offsetsLen; - offsetsLen += env.length + 1; - dataLen = addLengthsOfCStrings(dataLen, env); - } else { - envPos = -1; - } - - if (cwd != null) { - cwdPos = offsetsLen; - offsetsLen += 1; - // The +1 in the second argument can overflow only if the buffer contains 2^63-1 - // bytes, which is impossible since we are using Java arrays limited to 2^31-1. - dataLen = PythonUtils.addExact(dataLen, ((Buffer) cwd).length + 1L); - } else { - cwdPos = -1; - } - } catch (OverflowException e) { - throw newPosixException(invokeNode, OSErrorEnum.E2BIG.getNumber()); - } - - // This also guarantees that offsetsLen did not overflow: we add +1 to dataLen for each - // '\0', i.e. dataLen >= "number of strings" and offsetsLen < "number of strings" + 3 - // (3 accounts for the NULL terminating the executables, args and env arrays). - if (dataLen >= Integer.MAX_VALUE - 3) { - throw newPosixException(invokeNode, OSErrorEnum.E2BIG.getNumber()); - } - - byte[] data = new byte[(int) dataLen]; - long[] offsets = new long[offsetsLen]; - long offset = 0; - - offset = encodeCStringArray(data, offset, offsets, 0, executables); - offset = encodeCStringArray(data, offset, offsets, argsPos, args); - if (env != null) { - offset = encodeCStringArray(data, offset, offsets, envPos, env); - } - if (cwd != null) { - Buffer buf = (Buffer) cwd; - int strLen = (int) buf.length; - PythonUtils.arraycopy(buf.data, 0, data, (int) offset, strLen); - offsets[cwdPos] = offset; - offset += strLen + 1L; - } - assert offset == dataLen; - - int res = invokeNode.callInt(this, PosixNativeFunction.fork_exec, - data, offsets, offsets.length, argsPos, envPos, cwdPos, - stdinReadFd, stdinWriteFd, - stdoutReadFd, stdoutWriteFd, - stderrReadFd, stderrWriteFd, - errPipeReadFd, errPipeWriteFd, - closeFds ? 1 : 0, - restoreSignals ? 1 : 0, - callSetsid ? 1 : 0, - fdsToKeep, fdsToKeep.length); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public void execv(Object pathname, Object[] args, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - - // The following strings and string arrays need to be present in the native function: - // - char* - the pathname ('\0'-terminated string) - // - char** of arguments ('\0'-terminated strings with an extra NULL at the end) - // We do this by concatenating all strings (including their terminating '\0' characters) - // into one large byte buffer (which becomes 'char *') and pass an additional array of - // offsets to mark where the individual strings begin. To prevent memory allocation - // in C (and related free()), we reuse this array of integer offsets as an array of - // C-strings (char **). For this reason, the array of offsets is allocated as long[]. - // In the offsets array we mark the places where NULL should be with a special value -1. - // - the pathname is always at index 0 - // - the arguments start at index 1 - - // First we calculate the lengths of the offsets array and the string buffer (dataLen). - int offsetsLen = 1 + args.length + 1; - long pathnameLen = ((Buffer) pathname).length; - long dataLen; - - try { - // The +1 can overflow only if the buffer contains 2^63-1 bytes, which is impossible - // since we are using Java arrays limited to 2^31-1. - dataLen = addLengthsOfCStrings(pathnameLen + 1L, args); - } catch (OverflowException e) { - throw newPosixException(invokeNode, OSErrorEnum.E2BIG.getNumber()); - } - - // This also guarantees that offsetsLen did not overflow: we add +1 to dataLen for each - // '\0', i.e. dataLen >= "number of strings" and offsetsLen == "number of strings" + 1 - // (1 accounts for the NULL terminating the args array). - // Also, dataLen > pathnameLen, so this check makes sure that the cast of pathnameLen to int - // below is safe. - if (dataLen >= Integer.MAX_VALUE - 1) { - throw newPosixException(invokeNode, OSErrorEnum.E2BIG.getNumber()); - } - - byte[] data = new byte[(int) dataLen]; - long[] offsets = new long[offsetsLen]; - - PythonUtils.arraycopy(((Buffer) pathname).data, 0, data, 0, (int) pathnameLen); - long offset = encodeCStringArray(data, pathnameLen + 1L, offsets, 1, args); - assert offset == dataLen; - - invokeNode.call(this, PosixNativeFunction.call_execv, data, offsets, offsets.length); - throw getErrnoAndThrowPosixException(invokeNode); - } - - @ExportMessage - public int system(Object command, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_system, pathToCString(command)); - } - - private static long addLengthsOfCStrings(long prevLen, Object[] src) throws OverflowException { - long len = prevLen; - for (Object o : src) { - len = PythonUtils.addExact(len, ((Buffer) o).length); - } - return PythonUtils.addExact(len, src.length); // add space for terminating '\0' - } - - /** - * Copies null-terminated strings to a buffer {@code data} starting at position {@code offset}, - * and stores the offset of each string to the {@code offsets} array starting at index - * {@code startPos}. - */ - private static long encodeCStringArray(byte[] data, long startOffset, long[] offsets, int startPos, Object[] src) { - // The code that calculates dataLen already checked that there is no overflow and that all - // offsets fit into an int. - long offset = startOffset; - for (int i = 0; i < src.length; ++i) { - Buffer buf = (Buffer) src[i]; - int strLen = (int) buf.length; - PythonUtils.arraycopy(buf.data, 0, data, (int) offset, strLen); - offsets[startPos + i] = offset; - offset += strLen + 1; // +1 for the terminating \0 character - } - offsets[startPos + src.length] = -1; // this will become NULL in C (the char* array - // needs to be terminated by a NULL) - return offset; - } - - private static final class MMapHandle { - private final long pointer; - private final long length; - - public MMapHandle(long pointer, long length) { - this.pointer = pointer; - this.length = length; - } - } - - @ExportMessage - public Object mmap(long length, int prot, int flags, int fd, long offset, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long address = invokeNode.callLong(this, PosixNativeFunction.call_mmap, length, prot, flags, fd, offset); - if (address == 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return new MMapHandle(address, length); - } - - @ExportMessage - @SuppressWarnings("static-method") - public byte mmapReadByte(Object mmap, long index) { - MMapHandle handle = (MMapHandle) mmap; - if (index < 0 || index >= handle.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IndexOutOfBoundsException(); - } - return UNSAFE.getByte(handle.pointer + index); - } - - @ExportMessage - @SuppressWarnings("static-method") - public void mmapWriteByte(Object mmap, long index, byte value) { - MMapHandle handle = (MMapHandle) mmap; - checkIndexAndLen(handle, index, 1); - UNSAFE.putByte(handle.pointer + index, value); - } - - @ExportMessage - @SuppressWarnings("static-method") - public int mmapReadBytes(Object mmap, long index, byte[] bytes, int length) { - MMapHandle handle = (MMapHandle) mmap; - checkIndexAndLen(handle, index, length); - UNSAFE.copyMemory(null, handle.pointer + index, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length); - return length; - } - - @ExportMessage - @SuppressWarnings("static-method") - public void mmapWriteBytes(Object mmap, long index, byte[] bytes, int length) { - MMapHandle handle = (MMapHandle) mmap; - checkIndexAndLen(handle, index, length); - UNSAFE.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, handle.pointer + index, length); - } - - @ExportMessage - public void mmapFlush(Object mmap, long offset, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - MMapHandle handle = (MMapHandle) mmap; - checkIndexAndLen(handle, offset, length); - invokeNode.call(this, PosixNativeFunction.call_msync, handle.pointer, offset, length); - } - - @ExportMessage - public void mmapUnmap(Object mmap, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - MMapHandle handle = (MMapHandle) mmap; - if (length != handle.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException(); - } - int result = invokeNode.callInt(this, PosixNativeFunction.call_munmap, handle.pointer, length); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - @SuppressWarnings("static-method") - public long mmapGetPointer(Object mmap) { - MMapHandle handle = (MMapHandle) mmap; - return handle.pointer; - } - - private static void checkIndexAndLen(MMapHandle handle, long index, long length) { - if (length < 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException(); - } - if (index < 0 || index + length > handle.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IndexOutOfBoundsException(); - } - } - - @ExportMessage - public int socket(int domain, int type, int protocol, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_socket, domain, type, protocol); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public AcceptResult accept(int sockfd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - int result = invokeNode.callInt(this, PosixNativeFunction.call_accept, sockfd, addr.data, addr.len); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new AcceptResult(result, addr); - } - - @ExportMessage - public void bind(int sockfd, UniversalSockAddr usa, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; - int result = invokeNode.callInt(this, PosixNativeFunction.call_bind, sockfd, addr.data, addr.getLen()); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void connect(int sockfd, UniversalSockAddr usa, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; - int result = invokeNode.callInt(this, PosixNativeFunction.call_connect, sockfd, addr.data, addr.getLen()); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void listen(int sockfd, int backlog, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_listen, sockfd, backlog); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public UniversalSockAddr getpeername(int sockfd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - int result = invokeNode.callInt(this, PosixNativeFunction.call_getpeername, sockfd, addr.data, addr.len); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return addr; - } - - @ExportMessage - public UniversalSockAddr getsockname(int sockfd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - int result = invokeNode.callInt(this, PosixNativeFunction.call_getsockname, sockfd, addr.data, addr.len); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return addr; - } - - @ExportMessage - public int send(int sockfd, byte[] buf, int offset, int len, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - checkBounds(buf, offset, len); - int result = invokeNode.callInt(this, PosixNativeFunction.call_send, sockfd, buf, offset, len, flags); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public int sendto(int sockfd, byte[] buf, int offset, int len, int flags, UniversalSockAddr usa, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - checkBounds(buf, offset, len); - UniversalSockAddrImpl destAddr = (UniversalSockAddrImpl) usa; - int result = invokeNode.callInt(this, PosixNativeFunction.call_sendto, sockfd, buf, offset, len, flags, destAddr.data, destAddr.getLen()); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public int recv(int sockfd, byte[] buf, int offset, int len, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - checkBounds(buf, offset, len); - int result = invokeNode.callInt(this, PosixNativeFunction.call_recv, sockfd, buf, offset, len, flags); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public RecvfromResult recvfrom(int sockfd, byte[] buf, int offset, int len, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - checkBounds(buf, offset, len); - UniversalSockAddrImpl srcAddr = new UniversalSockAddrImpl(this); - int result = invokeNode.callInt(this, PosixNativeFunction.call_recvfrom, sockfd, buf, offset, len, flags, srcAddr.data, srcAddr.len); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new RecvfromResult(result, srcAddr); - } - - @ExportMessage - public void shutdown(int sockfd, int how, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_shutdown, sockfd, how); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int getsockopt(int sockfd, int level, int optname, byte[] optval, int optlen, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert optlen >= 0 && optval.length >= optlen; - int[] bufLen = new int[]{optlen}; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getsockopt, sockfd, level, optname, optval, bufLen); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return bufLen[0]; - } - - @ExportMessage - public void setsockopt(int sockfd, int level, int optname, byte[] optval, int optlen, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert optlen >= 0 && optval.length >= optlen; - int res = invokeNode.callInt(this, PosixNativeFunction.call_setsockopt, sockfd, level, optname, optval, optlen); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int inet_addr(Object src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_inet_addr, pathToCString(src)); - } - - @ExportMessage - public int inet_aton(Object src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws InvalidAddressException { - long r = invokeNode.callLong(this, PosixNativeFunction.call_inet_aton, pathToCString(src)); - if (r < 0) { - throw new InvalidAddressException(); - } - return (int) r; - } - - @ExportMessage - public Object inet_ntoa(int src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - Buffer buf = Buffer.allocate(INET_ADDRSTRLEN.value); - int len = invokeNode.callInt(this, PosixNativeFunction.call_inet_ntoa, src, buf.data); - return buf.withLength(len); - } - - @ExportMessage - public byte[] inet_pton(int family, Object src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException, InvalidAddressException { - byte[] buf = new byte[family == AF_INET.value ? 4 : 16]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_inet_pton, family, pathToCString(src), buf); - // Rather unusually, the return value of 0 does not indicate success but is used by - // inet_pton to report invalid format of the address (without setting errno). - // Success is reported by returning 1. - if (res == 1) { - return buf; - } - if (res == 0) { - throw new InvalidAddressException(); - } - throw getErrnoAndThrowPosixException(invokeNode); - } - - @ExportMessage - public Object inet_ntop(int family, byte[] src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if ((family == AF_INET.value && src.length < 4) || (family == AF_INET6.value && src.length < 16)) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException("Invalid length of IPv4/6 address"); - } - Buffer buf = Buffer.allocate(INET6_ADDRSTRLEN.value); - int res = invokeNode.callInt(this, PosixNativeFunction.call_inet_ntop, family, src, buf.data, INET6_ADDRSTRLEN.value); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return buf.withLength(findZero(buf.data)); - } - - @ExportMessage - public Object gethostname(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int maxLen = (HOST_NAME_MAX.defined ? HOST_NAME_MAX.getValueIfDefined() : _POSIX_HOST_NAME_MAX.value) + 1; - Buffer buf = Buffer.allocate(maxLen); - int res = invokeNode.callInt(this, PosixNativeFunction.call_gethostname, buf.data, maxLen); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return buf.withLength(findZero(buf.data)); - } - - @ExportMessage - public Object[] getnameinfo(UniversalSockAddr usa, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws GetAddrInfoException { - Buffer host = Buffer.allocate(NI_MAXHOST.value); - Buffer serv = Buffer.allocate(NI_MAXSERV.value); - UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getnameinfo, addr.data, addr.getLen(), host.data, NI_MAXHOST.value, serv.data, NI_MAXSERV.value, flags); - if (res != 0) { - throw new GetAddrInfoException(res, gai_strerror(res, invokeNode, fromByteArrayNode, switchEncodingFromUtf8Node)); - } - return new Object[]{ - host.withLength(findZero(host.data)), - serv.withLength(findZero(serv.data)), - }; - } - - @ExportMessage - public AddrInfoCursor getaddrinfo(Object node, Object service, int family, int sockType, int protocol, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws GetAddrInfoException { - long[] ptr = new long[1]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getaddrinfo, pathToCStringOrNull(node), pathToCStringOrNull(service), family, sockType, protocol, flags, ptr); - if (res != 0) { - throw new GetAddrInfoException(res, gai_strerror(res, invokeNode, fromByteArrayNode, switchEncodingFromUtf8Node)); - } - assert ptr[0] != 0; // getaddrinfo should return at least one result - return new AddrInfoCursorImpl(this, ptr[0], invokeNode); - } - - @ExportMessage - public TruffleString crypt(TruffleString word, TruffleString salt, - @Bind Node inliningTarget, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("toUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingToUtf8Node, - @Shared("tsCopyBytes") @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - /* - * We don't want to link the posix library with libcrypt, because it might not be available - * on the target system and would make the whole posix library fail to load. So we load it - * dynamically on demand. - */ - if (injectBranchProbability(SLOWPATH_PROBABILITY, cryptLibrary == null)) { - try { - cryptLibrary = InvokeNativeFunction.loadLibrary(inliningTarget, this, PythonLanguage.getPythonOS() != PythonOS.PLATFORM_DARWIN ? "libcrypt.so" : null); - } catch (Throwable e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw PRaiseNode.raiseStatic(invokeNode, PythonBuiltinClassType.SystemError, ErrorMessages.UNABLE_TO_LOAD_LIBCRYPT); - } - } - PosixNativeFunction function = PosixNativeFunction.crypt; - if (injectBranchProbability(SLOWPATH_PROBABILITY, cachedFunctions.get(function.ordinal()) == null)) { - InvokeNativeFunction.loadFunction(inliningTarget, this, cryptLibrary, function); - } - FunctionWithSignature funObject = cachedFunctions.get(function.ordinal()); - /* - * From the manpage: Upon successful completion, crypt returns a pointer to a string which - * encodes both the hashed passphrase, and the settings that were used to encode it. See - * crypt(5) for more detail on the format of hashed passphrases. crypt places its result in - * a static storage area, which will be overwritten by subsequent calls to crypt. It is not - * safe to call crypt from multiple threads simultaneously. Upon error, it may return a NULL - * pointer or a pointer to an invalid hash, depending on the implementation. - */ - // Note GIL is not enough as crypt is using global memory, so we need a really global lock - synchronized (CRYPT_LOCK) { - long resultPtr; - Object[] args = new Object[]{ - stringToUTF8CString(word, switchEncodingToUtf8Node, copyToByteArrayNode), - stringToUTF8CString(salt, switchEncodingToUtf8Node, copyToByteArrayNode)}; - try { - Object interopResult = invokeNode.functionInterop.call(funObject.signature(), funObject.function(), args); - resultPtr = invokeNode.getResultInterop().asLong(interopResult); - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - // CPython doesn't handle the case of "invalid hash" return specially and neither do we - if (resultPtr == 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - int len = 0; - while (UNSAFE.getByte(resultPtr + len) != 0) { - len++; - } - byte[] resultBytes = new byte[len]; - UNSAFE.copyMemory(null, resultPtr, resultBytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, len); - return createString(resultBytes, 0, resultBytes.length, false, fromByteArrayNode, switchEncodingFromUtf8Node); - } - } - - private TruffleString gai_strerror(int errorCode, InvokeNativeFunction invokeNode, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) { - byte[] buf = new byte[1024]; - invokeNode.call(this, PosixNativeFunction.call_gai_strerror, errorCode, buf, buf.length); - // TODO PyUnicode_DecodeLocale - return cStringToTruffleString(buf, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - /** - * Provides access to {@code struct addrinfo}. - * - * The layout of native {@code struct addrinfo} is as follows: - * - *
      -     * {@code
      -     *     struct addrinfo {
      -     *         int              ai_flags;           // intData[0]
      -     *         int              ai_family;          // intData[1]
      -     *         int              ai_socktype;        // intData[2]
      -     *         int              ai_protocol;        // intData[3]
      -     *         socklen_t        ai_addrlen;         // intData[4]
      -     *         struct sockaddr *ai_addr;            // data copied into socketAddress[]
      -     *         char            *ai_canonname;       // longData[0]
      -     *         struct addrinfo *ai_next;            // longData[1]
      -     *     };
      -     * }
      -     * 
      - * - * To avoid multiple NFI calls, we transfer the data in batch using arrays of {@code int}s and - * {@code long}s - int values are stored in {@code intData}, the {@code ai_canonname} and - * {@code ai_next} pointers are stored in {@code longData} and the socket address pointed to by - * {@code ai_addr} is copied into Java byte array {@code socketAddress}. We also cache two - * additional integers: - *
        - *
      • {@code intData[5]} contains {@code ai_addr->sa_family},
      • - *
      • {@code intData[6]} contains the length of {@code ai_canonname} if it is not {@code null} - *
      • - *
      - * - * It is not clear whether it is guaranteed that {@code ai_family} and - * {@code ai_addr->sa_family} are always the same. We provide both and use the later when - * decoding the socket address. - */ - private static class AddrInfo { - private final int[] intData = new int[7]; - private final long[] longData = new long[2]; - private byte[] socketAddress; - - private void update(long ptr, NFIPosixSupport nfiPosixSupport, InvokeNativeFunction invokeNode) { - socketAddress = new byte[(int) nfiPosixSupport.getConstant(SIZEOF_STRUCT_SOCKADDR_STORAGE)]; - int res = invokeNode.callInt(nfiPosixSupport, PosixNativeFunction.get_addrinfo_members, ptr, intData, longData, - socketAddress); - if (res != 0) { - throw shouldNotReachHere("the length of ai_canonname does not fit into an int"); - } - } - - int getFlags() { - return intData[0]; - } - - int getFamily() { - return intData[1]; - } - - int getSockType() { - return intData[2]; - } - - int getProtocol() { - return intData[3]; - } - - int getAddrLen() { - return intData[4]; - } - - int getAddrFamily() { - return intData[5]; - } - - int getCanonNameLen() { - assert getCanonNamePtr() != 0; - return intData[6]; - } - - long getCanonNamePtr() { - return longData[0]; - } - - long getNextPtr() { - return longData[1]; - } - } - - @ExportLibrary(AddrInfoCursorLibrary.class) - protected static class AddrInfoCursorImpl implements AddrInfoCursor { - - private final NFIPosixSupport nfiPosixSupport; - private long head; - private AddrInfo info; - - AddrInfoCursorImpl(NFIPosixSupport nfiPosixSupport, long head, InvokeNativeFunction invokeNode) { - this.nfiPosixSupport = nfiPosixSupport; - this.head = head; - info = new AddrInfo(); - info.update(head, nfiPosixSupport, invokeNode); - } - - @ExportMessage - void release(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - checkReleased(); - invokeNode.call(nfiPosixSupport, PosixNativeFunction.call_freeaddrinfo, head); - head = 0; - } - - @ExportMessage - boolean next(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - checkReleased(); - long nextPtr = info.getNextPtr(); - if (nextPtr == 0) { - return false; - } - info.update(nextPtr, nfiPosixSupport, invokeNode); - return true; - } - - @ExportMessage - int getFlags() { - checkReleased(); - return info.getFlags(); - } - - @ExportMessage - int getFamily() { - checkReleased(); - return info.getFamily(); - } - - @ExportMessage - int getSockType() { - checkReleased(); - return info.getSockType(); - } - - @ExportMessage - int getProtocol() { - checkReleased(); - return info.getProtocol(); - } - - @ExportMessage - Object getCanonName() { - checkReleased(); - long namePtr = info.getCanonNamePtr(); - if (namePtr == 0) { - return null; - } - int nameLen = info.getCanonNameLen(); - byte[] buf = new byte[nameLen]; - UNSAFE.copyMemory(null, namePtr, buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, nameLen); - return Buffer.wrap(buf); - } - - @ExportMessage - UniversalSockAddr getSockAddr() { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(nfiPosixSupport); - PythonUtils.arraycopy(info.socketAddress, 0, addr.data, 0, info.getAddrLen()); - addr.setFamily(info.getAddrFamily()); - addr.setLen(info.getAddrLen()); - return addr; - } - - private void checkReleased() { - if (head == 0) { - throw shouldNotReachHere("AddrInfoCursor has already been released"); - } - } - } - - @ExportMessage - UniversalSockAddr createUniversalSockAddrInet4(Inet4SockAddr src) { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - addr.setFamily(AF_INET.value); - ARRAY_ACCESSOR_BE.putShort(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT), (short) src.getPort()); - ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR) + getConstant(OFFSETOF_STRUCT_IN_ADDR_S_ADDR), src.getAddress()); - addr.setLen((int) getConstant(SIZEOF_STRUCT_SOCKADDR_IN)); - return addr; - } - - @ExportMessage - UniversalSockAddr createUniversalSockAddrInet6(Inet6SockAddr src) { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - addr.setFamily(AF_INET6.value); - ARRAY_ACCESSOR_BE.putShort(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT), (short) src.getPort()); - int addrOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR) + (int) getConstant(OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR); - PythonUtils.arraycopy(src.getAddress(), 0, addr.data, addrOffset, 16); - ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO), src.getFlowInfo()); - ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID), src.getScopeId()); - addr.setLen((int) getConstant(SIZEOF_STRUCT_SOCKADDR_IN6)); - return addr; - } - - @ExportMessage - UniversalSockAddr createUniversalSockAddrUnix(UnixSockAddr src) throws InvalidUnixSocketPathException { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - addr.setFamily(AF_UNIX.value); - byte[] path = src.getPath(); - if (path.length > getConstant(SIZEOF_STRUCT_SOCKADDR_UN_SUN_PATH)) { - throw InvalidUnixSocketPathException.INSTANCE; - } - int len = path.length + (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH); - PythonUtils.arraycopy(path, 0, addr.data, (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH), path.length); - addr.setLen(len); - return addr; - } - - @ExportLibrary(UniversalSockAddrLibrary.class) - protected static class UniversalSockAddrImpl implements UniversalSockAddr { - - private final NFIPosixSupport nfiPosixSupport; - private final byte[] data; - private final int[] len = new int[]{0}; - - UniversalSockAddrImpl(NFIPosixSupport nfiPosixSupport) { - this.nfiPosixSupport = nfiPosixSupport; - this.data = new byte[(int) getConstant(SIZEOF_STRUCT_SOCKADDR_STORAGE)]; - } - - @ExportMessage - int getFamily() { - int offset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY); - int size = (int) getConstant(SIZEOF_STRUCT_SOCKADDR_SA_FAMILY); - if (getLen() >= offset + size) { - if (size == 1) { - return Byte.toUnsignedInt(data[offset]); - } else if (size == 2) { - return Short.toUnsignedInt(ARRAY_ACCESSOR.getShort(data, offset)); - } else if (size == 4) { - return ARRAY_ACCESSOR.getInt(data, offset); - } else { - throw CompilerDirectives.shouldNotReachHere("Unexpected sizeof(sa_family_t)"); - } - } else { - return AF_UNSPEC.value; - } - } - - void setFamily(int family) { - int offset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY); - int size = (int) getConstant(SIZEOF_STRUCT_SOCKADDR_SA_FAMILY); - if (size == 1) { - data[offset] = (byte) family; - } else if (size == 2) { - ARRAY_ACCESSOR.putShort(data, offset, (short) family); - } else if (size == 4) { - ARRAY_ACCESSOR.putInt(data, offset, family); - } else { - throw CompilerDirectives.shouldNotReachHere("Unexpected sizeof(sa_family_t)"); - } - } - - @ExportMessage - Inet4SockAddr asInet4SockAddr() { - if (getFamily() != AF_INET.value) { - throw CompilerDirectives.shouldNotReachHere("Only AF_INET socket address can be converted to Inet4SockAddr"); - } - if (getLen() != getConstant(SIZEOF_STRUCT_SOCKADDR_IN)) { - throw CompilerDirectives.shouldNotReachHere("Wrong size of socket addr struct in asInet4SockAddr"); - } - int port = Short.toUnsignedInt(ARRAY_ACCESSOR_BE.getShort(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT))); - int address = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR) + getConstant(OFFSETOF_STRUCT_IN_ADDR_S_ADDR)); - return new Inet4SockAddr(port, address); - } - - @ExportMessage - Inet6SockAddr asInet6SockAddr() { - if (getFamily() != AF_INET6.value) { - throw CompilerDirectives.shouldNotReachHere("Only AF_INET6 socket address can be converted to Inet6SockAddr"); - } - if (getLen() != getConstant(SIZEOF_STRUCT_SOCKADDR_IN6)) { - throw CompilerDirectives.shouldNotReachHere("Wrong size of socket addr struct in asInet6SockAddr"); - } - int port = Short.toUnsignedInt(ARRAY_ACCESSOR_BE.getShort(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT))); - int addrOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR) + (int) getConstant(OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR); - byte[] address = PythonUtils.arrayCopyOfRange(data, addrOffset, addrOffset + 16); - int flowInfo = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO)); - int scopeId = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)); - return new Inet6SockAddr(port, address, flowInfo, scopeId); - } - - @ExportMessage - UnixSockAddr asUnixSockAddr() { - if (getFamily() != AF_UNIX.value) { - throw CompilerDirectives.shouldNotReachHere("Only AF_UNIX socket address can be converted to UnixSockAddr"); - } - int pathOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH); - byte[] pathBuf; - int linuxAddrLen = getLen() - pathOffset; - if (linuxAddrLen > 0 && data[pathOffset] == '\0') { - // Abstract Linux address - pathBuf = PythonUtils.arrayCopyOfRange(data, pathOffset, pathOffset + linuxAddrLen); - } else { - // Regular NULL-terminated string - int pathLen = -1; - for (int i = pathOffset; i < data.length; i++) { - if (data[i] == '\0') { - pathLen = i - pathOffset; - break; - } - } - assert pathLen >= 0; - pathBuf = PythonUtils.arrayCopyOfRange(data, pathOffset, pathOffset + pathLen); - } - return new UnixSockAddr(pathBuf); - } - - long getConstant(NFIPosixConstants constant) { - return nfiPosixSupport.getConstant(constant); - } - - int getLen() { - return len[0]; - } - - void setLen(int len) { - this.len[0] = len; - } - - } - - @ExportMessage - long semOpen(Object name, int openFlags, int mode, int value, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - Object ptr = invokeNode.call(this, PosixNativeFunction.call_sem_open, pathToCString(name), openFlags, mode, value); - if (invokeNode.getResultInterop().isNull(ptr)) { - throw getErrnoAndThrowPosixException(invokeNode); - } - try { - return invokeNode.getResultInterop().asPointer(ptr); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - @ExportMessage - void semClose(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_close, handle); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - void semUnlink(Object name, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_unlink, pathToCString(name)); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - private static final UnsupportedPosixFeatureException NO_SEM_GETVALUE_EXCEPTION = new UnsupportedPosixFeatureException("sem_getvalue is not available on the current platform"); - - @ExportMessage - int semGetValue(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - /* - * msimacek: It works on Linux, and it doesn't work on Darwin. It might work on some other - * Unix-likes, but it's hard to check, so let's assume it only works on Linux for now - */ - if (PythonLanguage.getPythonOS() != PythonOS.PLATFORM_LINUX) { - throw NO_SEM_GETVALUE_EXCEPTION; - } - int[] value = new int[1]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_getvalue, handle, value); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return value[0]; - } - - @ExportMessage - void semPost(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_post, handle); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - void semWait(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_wait, handle); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - boolean semTryWait(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_trywait, handle); - if (res < 0) { - int errno = getErrno(invokeNode); - if (errno == OSErrorEnum.EAGAIN.getNumber()) { - return false; - } - throw newPosixException(invokeNode, errno); - } - return true; - } - - @ExportMessage - boolean semTimedWait(long handle, long deadlineNs, - @Bind Node node, - @CachedLibrary("this") PosixSupportLibrary thisLib, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_LINUX) { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_timedwait, handle, deadlineNs); - if (res < 0) { - int errno = getErrno(invokeNode); - if (errno == OSErrorEnum.ETIMEDOUT.getNumber()) { - return false; - } - throw newPosixException(invokeNode, errno); - } - return true; - } else { - long deadlineMs = deadlineNs / 1_000_000; - while (true) { - if (thisLib.semTryWait(this, handle)) { - return true; - } - long currentMs = System.currentTimeMillis(); - if (currentMs > deadlineMs) { - return false; - } - long delayMs = Math.min(deadlineMs - currentMs, 20); - TruffleSafepoint.setBlockedThreadInterruptible(node, Thread::sleep, delayMs); - } - } - } - - @ExportMessage - @SuppressWarnings("static-method") - public PwdResult getpwuid(long uid, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - return getpw(PosixNativeFunction.call_getpwuid_r, uid, invokeNode, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - @ExportMessage - @SuppressWarnings("static-method") - public PwdResult getpwnam(Object name, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - return getpw(PosixNativeFunction.call_getpwname_r, pathToCString(name), invokeNode, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - @ExportMessage - @SuppressWarnings("static-method") - public boolean hasGetpwentries() { - return true; - } - - @ExportMessage - @SuppressWarnings("static-method") - public PwdResult[] getpwentries( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - // Note: this is not thread safe, so potentially problematic while running multiple contexts - // within one VM - int sysConfMax = getSysConfPwdSizeMax(invokeNode); - int initialBufferSize = sysConfMax == -1 ? 1024 : sysConfMax; - - ArrayList result = new ArrayList<>(); - invokeNode.call(this, PosixNativeFunction.call_setpwent); - long[] bufferSize = new long[1]; - long[] output = new long[PWD_OUTPUT_LEN]; - byte[] buffer = new byte[initialBufferSize]; - try { - while (true) { - Object pwPtr = invokeNode.call(this, PosixNativeFunction.call_getpwent, bufferSize); - if (invokeNode.getResultInterop().isNull(pwPtr)) { - break; - } - if (bufferSize[0] < 0 || bufferSize[0] > PWD_BUFFER_MAX_SIZE) { - throw outOfMemoryPosixError(); - } - if (buffer.length < bufferSize[0]) { - buffer = new byte[(int) bufferSize[0]]; - } - int code = invokeNode.callInt(this, PosixNativeFunction.get_getpwent_data, pwPtr, buffer, buffer.length, output); - if (code != 0) { - throw CompilerDirectives.shouldNotReachHere("get_getpwent_data failed"); - } - result.add(createPwdResult(buffer, output, fromByteArrayNode, switchEncodingFromUtf8Node)); - } - } finally { - invokeNode.call(this, PosixNativeFunction.call_endpwent); - } - return toPwdResultArray(result); - } - - @TruffleBoundary - private static PwdResult[] toPwdResultArray(ArrayList result) { - return result.toArray(new PwdResult[0]); - } - - private PwdResult getpw(PosixNativeFunction pwfun, Object pwfunArg, InvokeNativeFunction invokeNode, TruffleString.FromByteArrayNode fromByteArrayNode, - TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - int sysConfMax = getSysConfPwdSizeMax(invokeNode); - int bufferSize = sysConfMax == -1 ? 1024 : sysConfMax; - while (bufferSize < PWD_BUFFER_MAX_SIZE) { - byte[] data = new byte[bufferSize]; - long[] output = new long[PWD_OUTPUT_LEN]; - int result = invokeNode.callInt(this, pwfun, pwfunArg, data, data.length, output); - if (result == -1) { - return null; - } - if (result == 0) { - return createPwdResult(data, output, fromByteArrayNode, switchEncodingFromUtf8Node); - } - if (result != OSErrorEnum.ERANGE.getNumber() || sysConfMax != -1) { - // no point in trying larger buffer if we got different error or the OS already told - // us that sysConfMax should be enough... - throw newPosixException(invokeNode, result); - } - bufferSize <<= 1; - } - throw outOfMemoryPosixError(); - } - - private static PwdResult createPwdResult(byte[] data, long[] output, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) - throws PosixException { - return new PwdResult( - extractZeroTerminatedString(data, output[0], fromByteArrayNode, switchEncodingFromUtf8Node), - output[1], output[2], - extractZeroTerminatedString(data, output[3], fromByteArrayNode, switchEncodingFromUtf8Node), - extractZeroTerminatedString(data, output[4], fromByteArrayNode, switchEncodingFromUtf8Node)); - } - - @ExportMessage - public int ioctlBytes(int fd, long request, byte[] arg, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_ioctl_bytes, fd, request, arg); - if (res < 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return res; - } - - @ExportMessage - public int ioctlInt(int fd, long request, int arg, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_ioctl_int, fd, request, arg); - if (res < 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return res; - } - - private static TruffleString extractZeroTerminatedString(byte[] buffer, long longOffset, TruffleString.FromByteArrayNode fromByteArrayNode, - TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - if (longOffset < 0 || longOffset >= buffer.length) { - throw outOfMemoryPosixError(); - } - int offset = (int) longOffset; - int end = offset; - while (end < buffer.length && buffer[end] != '\0') { - end++; - } - if (end == buffer.length) { - throw CompilerDirectives.shouldNotReachHere("Could not find the end of the string"); - } - // TODO PyUnicode_DecodeFSDefault - return createString(buffer, offset, end - offset, true, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - private static PosixException outOfMemoryPosixError() throws PosixException { - throw new PosixException(OSErrorEnum.ENOMEM.getNumber(), OSErrorEnum.ENOMEM.getMessage()); - } - - private int sysConfPwdSizeMax = -1; - - private int getSysConfPwdSizeMax(InvokeNativeFunction invokeNode) throws PosixException { - if (CompilerDirectives.injectBranchProbability(SLOWPATH_PROBABILITY, sysConfPwdSizeMax == -1)) { - long sysConfMaxLong = invokeNode.callLong(this, PosixNativeFunction.get_sysconf_getpw_r_size_max); - if (sysConfMaxLong != -1 && (sysConfMaxLong < 0 || sysConfMaxLong > PWD_BUFFER_MAX_SIZE)) { - throw outOfMemoryPosixError(); - } - sysConfPwdSizeMax = (int) sysConfMaxLong; - } - return sysConfPwdSizeMax; - } - - // ------------------ - // Path conversions - - @ExportMessage - @SuppressWarnings("static-method") - public Object createPathFromString(TruffleString path, - @Shared("toUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingNode, - @Shared("tsCopyBytes") @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { - return checkPath(getStringBytes(path, switchEncodingNode, copyToByteArrayNode)); - } - - @ExportMessage - @SuppressWarnings("static-method") - public Object createPathFromBytes(byte[] path) { - return checkPath(path); - } - - @ExportMessage - @SuppressWarnings("static-method") - public TruffleString getPathAsString(Object path, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { - Buffer result = (Buffer) path; - if (result.length > Integer.MAX_VALUE) { - // sanity check that it is safe to cast result.length to int, to be removed once - // we support large arrays - throw CompilerDirectives.shouldNotReachHere("Posix path cannot fit into a Java array"); - } - return createString(result.data, 0, (int) result.length, true, fromByteArrayNode, switchEncodingNode); - } - - @ExportMessage - @SuppressWarnings("static-method") - public Buffer getPathAsBytes(Object path) { - return (Buffer) path; - } - - private static TruffleString createString(byte[] src, int offset, int length, boolean copy, TruffleString.FromByteArrayNode fromByteArrayNode, - TruffleString.SwitchEncodingNode switchEncodingNode) { - // TODO PyUnicode_DecodeFSDefault - TruffleString utf8 = fromByteArrayNode.execute(src, offset, length, UTF_8, copy); - return switchEncodingNode.execute(utf8, TS_ENCODING); - } - - private static byte[] getStringBytes(TruffleString str, TruffleString.SwitchEncodingNode switchEncodingNode, TruffleString.CopyToByteArrayNode copyToByteArrayNode) { - // TODO replace getBytes with PyUnicode_FSConverter equivalent - TruffleString utf8 = switchEncodingNode.execute(str, UTF_8); - byte[] bytes = new byte[utf8.byteLength(UTF_8)]; - copyToByteArrayNode.execute(utf8, 0, bytes, 0, bytes.length, UTF_8); - return bytes; - } - - private static Buffer checkPath(byte[] path) { - for (byte b : path) { - if (b == 0) { - return null; - } - } - // TODO we keep a byte[] provided by the caller, who can potentially change it, making our - // check for embedded nulls pointless. Maybe we should copy it and while on it, might as - // well add the terminating null character, avoiding the copy we do later in pathToCString. - return Buffer.wrap(path); - } - - // ------------------ - // Objects/handles/pointers - - protected static class DirEntry { - final Buffer name; - final long ino; - final int type; - - DirEntry(Buffer name, long ino, int type) { - this.name = name; - this.ino = ino; - this.type = type; - } - - @Override - public String toString() { - return "DirEntry{" + - "name='" + new String(name.data, 0, (int) name.length) + "'" + - ", ino=" + ino + - ", type=" + type + - '}'; - } - } - - // ------------------ - // Helpers - - private int getErrno(InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.get_errno); - } - - private void setErrno(InvokeNativeFunction invokeNode, int errno) { - invokeNode.call(this, PosixNativeFunction.set_errno, errno); - } - - private PosixException getErrnoAndThrowPosixException(InvokeNativeFunction invokeNode) throws PosixException { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - - @TruffleBoundary - private PosixException newPosixException(InvokeNativeFunction invokeNode, int errno) throws PosixException { - throw new PosixException(errno, strerror(errno, invokeNode, TruffleString.FromByteArrayNode.getUncached(), TruffleString.SwitchEncodingNode.getUncached())); - } - - private Object wrap(long[] value) { - if (value == null) { - return PNone.NO_VALUE; - } else { - return value; - } - } - - private Object wrap(Timeval[] timeval) { - if (timeval == null) { - return PNone.NO_VALUE; - } else { - return new long[]{timeval[0].getSeconds(), timeval[0].getMicroseconds(), timeval[1].getSeconds(), timeval[1].getMicroseconds()}; - } - } - - private static TruffleString cStringToTruffleString(byte[] buf, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingNode) { - return createString(buf, 0, findZero(buf), true, fromByteArrayNode, switchEncodingNode); - } - - private static int findZero(byte[] buf) { - for (int i = 0; i < buf.length; ++i) { - if (buf[i] == 0) { - return i; - } - } - return buf.length; - } - - private Object pathToCStringOrNull(Object path) { - return path == null ? PNone.NO_VALUE : bufferToCString((Buffer) path); - } - - private Object pathToCString(Object path) { - return bufferToCString((Buffer) path); - } - - private Object bufferToCString(Buffer path) { - return nullTerminate(path.data, (int) path.length); - } - - private Object stringToUTF8CString(TruffleString input, - @Cached TruffleString.SwitchEncodingNode switchEncodingToUtf8Node, - @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { - byte[] utf8 = getStringBytes(input, switchEncodingToUtf8Node, copyToByteArrayNode); - return nullTerminate(utf8, utf8.length); - } - - private static byte[] nullTerminate(byte[] str, int length) { - byte[] terminated = new byte[length + 1]; - PythonUtils.arraycopy(str, 0, terminated, 0, length); - return terminated; - } - - private static void checkBounds(byte[] buf, int offset, int length) { - if (length < 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException(); - } - if (offset < 0 || offset + length > buf.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IndexOutOfBoundsException(); - } - } - - @TruffleBoundary - private static void log(Level level, String fmt, Object... args) { - if (LOGGER.isLoggable(level)) { - LOGGER.log(level, String.format(fmt, args)); - } - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIZlibSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIZlibSupport.java deleted file mode 100644 index 767d3d6399..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIZlibSupport.java +++ /dev/null @@ -1,696 +0,0 @@ -/* - * 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 - * - * 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; - -import com.oracle.graal.python.PythonLanguage; -import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.ThreadLocalAction.Access; -import com.oracle.truffle.api.TruffleLogger; -import com.oracle.truffle.api.strings.TruffleString; - -/*- - * Generated using: - * scripts/nfi_gen.py -name Zlib -cpath graalpython/com.oracle.graal.python.cext/zlib/zlib.c -lib libzsupport - */ -public class NFIZlibSupport { - - private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NFIZlibSupport.class); - - public static final int NO_ERROR = 0; - public static final int DEFLATE_INIT_ERROR = 101; - public static final int DEFLATE_END_ERROR = 102; - public static final int DEFLATE_DICT_ERROR = 103; - public static final int DEFLATE_OBJ_ERROR = 104; - public static final int DEFLATE_FLUSH_ERROR = 105; - public static final int DEFLATE_COPY_ERROR = 106; - public static final int DEFLATE_ERROR = 107; - public static final int INFLATE_INIT_ERROR = 201; - public static final int INFLATE_END_ERROR = 202; - public static final int INFLATE_DICT_ERROR = 203; - public static final int INFLATE_OBJ_ERROR = 204; - public static final int INFLATE_FLUSH_ERROR = 205; - public static final int INFLATE_COPY_ERROR = 206; - public static final int INFLATE_ERROR = 207; - public static final int INCOMPLETE_ERROR = 99; - public static final int MEMORY_ERROR = 999; - public static final int OUTPUT_OPTION = 0; - public static final int UNUSED_DATA_OPTION = 1; - public static final int UNCONSUMED_TAIL_OPTION = 2; - public static final int ZDICT_OPTION = 3; - - enum ZlibNativeFunctions implements NativeLibrary.NativeFunction { - - /*- - nfi_function: name('zlibVersion') static(true) - char *zlib_get_version() - */ - zlib_get_version("(): STRING"), - - /*- - nfi_function: name('zlibRuntimeVersion') static(true) - char *zlib_get_runtime_version() - */ - zlib_get_runtime_version("(): STRING"), - - /*- - nfi_function: name('crc32') - uLong zlib_crc32(uLong crc, Byte *buf, uInt len) - */ - zlib_crc32("(UINT64, [UINT8], UINT32): UINT64"), - - /*- - nfi_function: name('adler32') - uLong zlib_adler32(uLong crc, Byte *buf, uInt len) - */ - zlib_adler32("(UINT64, [UINT8], UINT32): UINT64"), - - /*- - nfi_function: name('createStream') map('zlib_stream*', 'POINTER') - zlib_stream *zlib_create_zlib_stream() - */ - zlib_create_zlib_stream("(): POINTER"), - - /*- - nfi_function: name('getTimeElapsed') map('zlib_stream*', 'POINTER') static(true) - double zlib_get_timeElapsed(zlib_stream* zst) - */ - zlib_get_timeElapsed("(POINTER): DOUBLE"), - - /*- - nfi_function: name('deallocateStream') map('zlib_stream*', 'POINTER') - void zlib_free_stream(zlib_stream* zst) - */ - zlib_free_stream("(POINTER): VOID"), - - /*- - nfi_function: name('gcReleaseHelper') map('zlib_stream*', 'POINTER') release(true) - void zlib_gc_helper(zlib_stream* zst) - */ - zlib_gc_helper("(POINTER): VOID"), - - /*- - nfi_function: name('getErrorFunction') map('zlib_stream*', 'POINTER') - int zlib_get_error_type(zlib_stream *zst) - */ - zlib_get_error_type("(POINTER): SINT32"), - - /*- - nfi_function: name('getStreamErrorMsg') map('zlib_stream*', 'POINTER') - char *zlib_get_stream_msg(zlib_stream *zst) - */ - zlib_get_stream_msg("(POINTER): STRING"), - - /*- - nfi_function: name('hasStreamErrorMsg') map('zlib_stream*', 'POINTER') - int zlib_has_stream_msg(zlib_stream *zst) - */ - zlib_has_stream_msg("(POINTER): SINT32"), - - /*- - nfi_function: name('getEOF') map('zlib_stream*', 'POINTER') - int zlib_get_eof(zlib_stream *zst) - */ - zlib_get_eof("(POINTER): SINT32"), - - /*- - nfi_function: name('getIsInitialised') map('zlib_stream*', 'POINTER') - int zlib_get_is_initialised(zlib_stream *zst) - */ - zlib_get_is_initialised("(POINTER): SINT32"), - - /*- - nfi_function: name('getBufferSize') map('zlib_stream*', 'POINTER') - uInt zlib_get_buffer_size(zlib_stream *zst, int option) - */ - zlib_get_buffer_size("(POINTER, SINT32): UINT32"), - - /*- - nfi_function: name('getBuffer') map('zlib_stream*', 'POINTER') - void zlib_get_off_heap_buffer(zlib_stream *zst, int option, Byte *dest) - */ - zlib_get_off_heap_buffer("(POINTER, SINT32, [UINT8]): VOID"), - - /*- - nfi_function: name('createCompObject') map('zlib_stream*', 'POINTER') - zlib_stream *zlib_create_compobject() - */ - zlib_create_compobject("(): POINTER"), - - /*- - nfi_function: name('deflateOffHeap') map('zlib_stream*', 'POINTER') - int zlib_deflate_off_heap(zlib_stream *zst, Byte *in, ssize_t in_len, ssize_t buf_size, int level) - */ - zlib_deflate_off_heap("(POINTER, [UINT8], SINT64, SINT64, SINT32, SINT32): SINT32"), - - /*- - nfi_function: name('inflateOffHeap') map('zlib_stream*', 'POINTER') - int zlib_inflate_off_heap(zlib_stream *zst, Byte *in, ssize_t in_len, ssize_t buf_size, int wbits) - */ - zlib_inflate_off_heap("(POINTER, [UINT8], SINT64, SINT64, SINT32): SINT32"), - - /*- - nfi_function: name('compressObjInitWithDict') map('zlib_stream*', 'POINTER') - int zlib_Compress_init(zlib_stream *zst, int level, int method,int wbits, int memLevel,int strategy, Byte *dict, size_t dict_len) - */ - zlib_Compress_init("(POINTER, SINT32, SINT32, SINT32, SINT32, SINT32, [UINT8], UINT64): SINT32"), - - /*- - nfi_function: name('compressObjInit') map('zlib_stream*', 'POINTER') - int zlib_Compress_init_no_dict(zlib_stream *zst, int level, int method,int wbits, int memLevel,int strategy) - */ - zlib_Compress_init_no_dict("(POINTER, SINT32, SINT32, SINT32, SINT32, SINT32): SINT32"), - - /*- - nfi_function: name('compressObj') map('zlib_stream*', 'POINTER') - int zlib_Compress_obj(zlib_stream *zst, Byte *in, ssize_t in_len, ssize_t buf_size) - */ - zlib_Compress_obj("(POINTER, [UINT8], SINT64, SINT64): SINT32"), - - /*- - nfi_function: name('compressObjFlush') map('zlib_stream*', 'POINTER') - int zlib_Compress_flush(zlib_stream *zst, Byte *in, ssize_t buf_size, int mode) - */ - zlib_Compress_flush("(POINTER, [UINT8], SINT64, SINT32): SINT32"), - - /*- - nfi_function: name('compressObjCopy') map('zlib_stream*', 'POINTER') - int zlib_Compress_copy(zlib_stream *zst, zlib_stream *new_copy) - */ - zlib_Compress_copy("(POINTER, POINTER): SINT32"), - - /*- - nfi_function: name('decompressObjInitWithDict') map('zlib_stream*', 'POINTER') - int zlib_Decompress_init(zlib_stream *zst, int wbits, Byte *dict, size_t dict_len) - */ - zlib_Decompress_init("(POINTER, SINT32, [UINT8], UINT64): SINT32"), - - /*- - nfi_function: name('decompressObjInit') map('zlib_stream*', 'POINTER') - int zlib_Decompress_init_no_dict(zlib_stream *zst, int wbits) - */ - zlib_Decompress_init_no_dict("(POINTER, SINT32): SINT32"), - - /*- - nfi_function: name('decompressObj') map('zlib_stream*', 'POINTER') - int zlib_Decompress_obj(zlib_stream *zst, Byte *in, ssize_t in_len, ssize_t buf_size, ssize_t max_length) - */ - zlib_Decompress_obj("(POINTER, [UINT8], SINT64, SINT64, SINT64): SINT32"), - - /*- - nfi_function: name('decompressObjFlush') map('zlib_stream*', 'POINTER') - int zlib_Decompress_flush(zlib_stream *zst, ssize_t length) - */ - zlib_Decompress_flush("(POINTER, SINT64): SINT32"), - - /*- - nfi_function: name('decompressObjCopy') map('zlib_stream*', 'POINTER') - int zlib_Decompress_copy(zlib_stream *zst, zlib_stream *new_copy) - */ - zlib_Decompress_copy("(POINTER, POINTER): SINT32"), - - zlib_decompress("(POINTER, [UINT8], SINT64, SINT64): SINT32"); - - private final String signature; - - ZlibNativeFunctions(String signature) { - this.signature = signature; - } - - @Override - public String signature() { - return signature; - } - } - - private static final String SUPPORTING_NATIVE_LIB_NAME = "zsupport"; - - private final PythonContext pythonContext; - private final NativeLibrary.TypedNativeLibrary typedNativeLib; - - @CompilerDirectives.CompilationFinal private boolean available; - - private NFIZlibSupport(PythonContext context, NativeLibrary.NFIBackend backend, String noNativeAccessHelp) { - if (context.useNativeCompressionModules()) { - this.pythonContext = context; - this.typedNativeLib = NativeLibrary.create(PythonContext.getSupportLibName(SUPPORTING_NATIVE_LIB_NAME), - ZlibNativeFunctions.values(), backend, noNativeAccessHelp, true); - this.available = true; - } else { - this.pythonContext = null; - this.typedNativeLib = null; - this.available = false; - } - } - - public static NFIZlibSupport createNative(PythonContext context, String noNativeAccessHelp) { - return new NFIZlibSupport(context, NativeLibrary.NFIBackend.NATIVE, noNativeAccessHelp); - } - - public static NFIZlibSupport createLLVM(PythonContext context, String noNativeAccessHelp) { - return new NFIZlibSupport(context, NativeLibrary.NFIBackend.LLVM, noNativeAccessHelp); - } - - static class PointerReleaseCallback implements AsyncHandler.AsyncAction { - private final Pointer pointer; - - public PointerReleaseCallback(Pointer pointer) { - this.pointer = pointer; - } - - @Override - public void execute(PythonContext context, Access access) { - synchronized (pointer) { - if (pointer.isReleased()) { - return; - } - try { - pointer.doRelease(); - pointer.markReleased(); - LOGGER.finest("NFIZlibSupport pointer has been freed"); - } catch (Exception e) { - LOGGER.severe("Error while trying to free NFIZlibSupport pointer: " + e.getMessage()); - } - } - } - } - - public static class Pointer extends AsyncHandler.SharedFinalizer.FinalizableReference { - - private final NFIZlibSupport lib; - - public Pointer(Object referent, Object ptr, NFIZlibSupport lib) { - super(referent, ptr, lib.pythonContext.getSharedFinalizer()); - this.lib = lib; - } - - protected void doRelease() { - lib.gcReleaseHelper(getReference()); - } - - @Override - public AsyncHandler.AsyncAction release() { - if (!isReleased()) { - return new PointerReleaseCallback(this); - } - return null; - } - } - - @TruffleBoundary - public void notAvailable() { - if (available) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - CompilerAsserts.neverPartOfCompilation("Checking NFIZlibSupport availability should only be done during initialization."); - available = false; - } - } - - public boolean isAvailable() { - return available; - } - - @TruffleBoundary - public void setAvailable() { - if (!available && typedNativeLib != null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - CompilerAsserts.neverPartOfCompilation("Checking NFIZlibSupport availability should only be done during initialization."); - available = true; - } - } - - /** - * - * - * @return char* - */ - public Object zlibVersion() { - return typedNativeLib.callUncached(pythonContext, ZlibNativeFunctions.zlib_get_version); - } - - /** - * - * - * @return char* - */ - public Object zlibRuntimeVersion() { - return typedNativeLib.callUncached(pythonContext, ZlibNativeFunctions.zlib_get_runtime_version); - } - - /** - * - * @param zst zlib_stream* zst - * @return double - */ - public Object getTimeElapsed(Object zst) { - return typedNativeLib.callUncached(pythonContext, ZlibNativeFunctions.zlib_get_timeElapsed, zst); - } - - /** - * - * @param zst zlib_stream* zst - * - */ - public Object gcReleaseHelper(Object zst) { - return typedNativeLib.callUncached(pythonContext, ZlibNativeFunctions.zlib_gc_helper, zst); - } - - /** - * - * @param crc uLong crc - * @param buf Byte *buf - * @param len uInt len - * @return uLong - */ - public long crc32(long crc, byte[] buf, int len, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, ZlibNativeFunctions.zlib_crc32, crc, buf, len); - } - - /** - * - * @param crc uLong crc - * @param buf Byte *buf - * @param len uInt len - * @return uLong - */ - public long adler32(long crc, byte[] buf, int len, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callLong(typedNativeLib, ZlibNativeFunctions.zlib_adler32, crc, buf, len); - } - - /** - * - * - * @return zlib_stream* - */ - public Object createStream( - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.call(typedNativeLib, ZlibNativeFunctions.zlib_create_zlib_stream); - } - - /** - * - * @param zst zlib_stream* zst - * - */ - public void deallocateStream(Object zst, - NativeLibrary.InvokeNativeFunction invokeNode) { - invokeNode.call(typedNativeLib, ZlibNativeFunctions.zlib_free_stream, zst); - } - - /** - * - * @param zst zlib_stream *zst - * @return int - */ - public int getErrorFunction(Object zst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_get_error_type, zst); - } - - /** - * - * @param zst zlib_stream *zst - * @return char* - */ - public TruffleString getStreamErrorMsg(Object zst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callString(typedNativeLib, ZlibNativeFunctions.zlib_get_stream_msg, zst); - } - - /** - * - * @param zst zlib_stream *zst - * @return int - */ - public int hasStreamErrorMsg(Object zst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_has_stream_msg, zst); - } - - /** - * - * @param zst zlib_stream *zst - * @return int - */ - public int getEOF(Object zst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_get_eof, zst); - } - - /** - * - * @param zst zlib_stream *zst - * @return int - */ - public int getIsInitialised(Object zst, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_get_is_initialised, zst); - } - - /** - * - * @param zst zlib_stream *zst - * @param option int option - * @return uInt - */ - public int getBufferSize(Object zst, int option, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_get_buffer_size, zst, option); - } - - /** - * - * @param zst zlib_stream *zst - * @param option int option - * @param dest Byte *dest - * - */ - public void getBuffer(Object zst, int option, byte[] dest, - NativeLibrary.InvokeNativeFunction invokeNode) { - invokeNode.call(typedNativeLib, ZlibNativeFunctions.zlib_get_off_heap_buffer, zst, option, dest); - } - - /** - * - * - * @return zlib_stream* - */ - public Object createCompObject( - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.call(typedNativeLib, ZlibNativeFunctions.zlib_create_compobject); - } - - /** - * - * @param zst zlib_stream *zst - * @param in Byte *in - * @param in_len ssize_t in_len - * @param buf_size ssize_t buf_size - * @param level int level - * @param wbits int window bits - * @return int - */ - public int deflateOffHeap(Object zst, byte[] in, long in_len, long buf_size, int level, int wbits, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_deflate_off_heap, zst, in, in_len, buf_size, level, wbits); - } - - /** - * - * @param zst zlib_stream *zst - * @param in Byte *in - * @param in_len ssize_t in_len - * @param buf_size ssize_t buf_size - * @param wbits int wbits - * @return int - */ - public int inflateOffHeap(Object zst, byte[] in, long in_len, long buf_size, int wbits, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_inflate_off_heap, zst, in, in_len, buf_size, wbits); - } - - /** - * - * @param zst zlib_stream *zst - * @param level int level - * @param method int method - * @param wbits int wbits - * @param memLevel int memLevel - * @param strategy int strategy - * @param dict Byte *dict - * @param dict_len size_t dict_len - * @return int - */ - public int compressObjInitWithDict(Object zst, int level, int method, int wbits, int memLevel, int strategy, byte[] dict, long dict_len, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Compress_init, zst, level, method, wbits, memLevel, strategy, dict, dict_len); - } - - /** - * - * @param zst zlib_stream *zst - * @param level int level - * @param method int method - * @param wbits int wbits - * @param memLevel int memLevel - * @param strategy int strategy - * @return int - */ - public int compressObjInit(Object zst, int level, int method, int wbits, int memLevel, int strategy, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Compress_init_no_dict, zst, level, method, wbits, memLevel, strategy); - } - - /** - * - * @param zst zlib_stream *zst - * @param in Byte *in - * @param in_len ssize_t in_len - * @param buf_size ssize_t buf_size - * @return int - */ - public int compressObj(Object zst, Object in, long in_len, long buf_size, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Compress_obj, zst, in, in_len, buf_size); - } - - /** - * - * @param zst zlib_stream *zst - * @param in Byte *in - * @param buf_size ssize_t buf_size - * @param mode int mode - * @return int - */ - public int compressObjFlush(Object zst, byte[] in, long buf_size, int mode, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Compress_flush, zst, in, buf_size, mode); - } - - /** - * - * @param zst zlib_stream *zst - * @param new_copy zlib_stream *new_copy - * @return int - */ - public int compressObjCopy(Object zst, Object new_copy, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Compress_copy, zst, new_copy); - } - - /** - * - * @param zst zlib_stream *zst - * @param wbits int wbits - * @param dict Byte *dict - * @param dict_len size_t dict_len - * @return int - */ - public int decompressObjInitWithDict(Object zst, int wbits, byte[] dict, long dict_len, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Decompress_init, zst, wbits, dict, dict_len); - } - - /** - * - * @param zst zlib_stream *zst - * @param wbits int wbits - * @return int - */ - public int decompressObjInit(Object zst, int wbits, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Decompress_init_no_dict, zst, wbits); - } - - /** - * - * @param zst zlib_stream *zst - * @param in Byte *in - * @param in_len ssize_t in_len - * @param buf_size ssize_t buf_size - * @param max_length ssize_t max_length - * @return int - */ - public int decompressObj(Object zst, byte[] in, long in_len, long buf_size, long max_length, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Decompress_obj, zst, in, in_len, buf_size, max_length); - } - - /** - * - * @param zst zlib_stream *zst - * @param length ssize_t length - * @return int - */ - public int decompressObjFlush(Object zst, long length, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Decompress_flush, zst, length); - } - - /** - * - * @param zst zlib_stream *zst - * @param new_copy zlib_stream *new_copy - * @return int - */ - public int decompressObjCopy(Object zst, Object new_copy, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_Decompress_copy, zst, new_copy); - } - - /** - * returns needs_input flag if the value isn't less than 0, otherwise it returns the zlib error - * code. - * - * @param zst zlib_stream *zst - * @param data Byte *data - * @param len size_t len - * @param max_length ssize_t max_length - * @return int - */ - public int decompressor(Object zst, byte[] data, long len, long max_length, - NativeLibrary.InvokeNativeFunction invokeNode) { - return invokeNode.callInt(typedNativeLib, ZlibNativeFunctions.zlib_decompress, zst, data, len, max_length); - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeBz2Support.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeBz2Support.java new file mode 100644 index 0000000000..b7230b8d3f --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeBz2Support.java @@ -0,0 +1,242 @@ +/* + * 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.runtime; + +import static com.oracle.graal.python.annotations.NativeSimpleType.DOUBLE; +import static com.oracle.graal.python.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT32; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT64; +import static com.oracle.graal.python.annotations.NativeSimpleType.VOID; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.annotations.DowncallSignature; +import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; +import com.oracle.truffle.api.ThreadLocalAction.Access; +import com.oracle.truffle.api.TruffleLogger; + +public class NativeBz2Support extends NativeCompressionSupport { + + private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NativeBz2Support.class); + private static final String SUPPORTING_NATIVE_LIB_NAME = "bz2support"; + + abstract static class Bz2NativeFunctions { + @DowncallSignature(returnType = POINTER) + abstract long bz_create_bzst_stream(); + + @DowncallSignature(returnType = DOUBLE, argumentTypes = {POINTER}) + abstract double bz_get_timeElapsed(long bzst); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER}) + abstract void bz_free_stream(long bzst); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER}) + abstract void bz_gc_helper(long bzst); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long bz_get_next_in_index(long bzst); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long bz_get_bzs_avail_in_real(long bzst); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER, SINT64}) + abstract void bz_set_bzs_avail_in_real(long bzst, long v); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long bz_get_output_buffer_size(long bzst); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER, POINTER}) + abstract void bz_get_output_buffer(long bzst, long dest); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32}) + abstract int bz_compressor_init(long bzst, int compresslevel); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT32, SINT64}) + abstract int bz_compress(long bzst, long data, long len, int action, long bufsize); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int bz_decompress_init(long bzst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT64, SINT64, SINT64}) + abstract int bz_decompress(long bzst, long inputBuffer, long offset, long maxLength, long bufsize, long bzsAvailInReal); + + static NativeLibrary loadNativeLibrary(PythonContext context) { + return NativeCompressionSupport.loadNativeLibrary(context, SUPPORTING_NATIVE_LIB_NAME); + } + } + + private final Bz2NativeFunctions nativeFunctions; + + private NativeBz2Support(PythonContext context) { + super(context); + this.nativeFunctions = isAvailable() ? new Bz2NativeFunctionsGen(context) : null; + } + + public static NativeBz2Support createNative(PythonContext context, String noNativeAccessHelp) { + return new NativeBz2Support(context); + } + + static class PointerReleaseCallback implements AsyncHandler.AsyncAction { + private final Pointer pointer; + + public PointerReleaseCallback(Pointer pointer) { + this.pointer = pointer; + } + + @Override + public void execute(PythonContext context, Access access) { + if (!pointer.markReleased()) { + assert pointer.isReleased(); + return; + } + try { + pointer.doRelease(); + LOGGER.finest("NativeBz2Support pointer has been freed"); + } catch (Exception e) { + LOGGER.severe("Error while trying to free NativeBz2Support pointer: " + e.getMessage()); + } + } + } + + public static class Pointer extends AsyncHandler.SharedFinalizer.FinalizableReference { + + private final NativeBz2Support lib; + private final long pointer; + + public Pointer(Object referent, long pointer, NativeBz2Support lib) { + super(referent, lib.pythonContext.getSharedFinalizer()); + this.lib = lib; + this.pointer = pointer; + } + + public long getPointer() { + return pointer; + } + + protected void doRelease() { + lib.gcReleaseHelper(pointer); + } + + @Override + public AsyncHandler.AsyncAction release() { + if (!isReleased()) { + return new PointerReleaseCallback(this); + } + return null; + } + } + + public Object getTimeElapsed(long zst) { + return nativeFunctions.bz_get_timeElapsed(zst); + } + + public Object gcReleaseHelper(long bzst) { + nativeFunctions.bz_gc_helper(bzst); + return null; + } + + public long createStream() { + return nativeFunctions.bz_create_bzst_stream(); + } + + public void deallocateStream(long bzst) { + nativeFunctions.bz_free_stream(bzst); + } + + public long getNextInIndex(long bzst) { + return nativeFunctions.bz_get_next_in_index(bzst); + } + + public long getBzsAvailInReal(long bzst) { + return nativeFunctions.bz_get_bzs_avail_in_real(bzst); + } + + public void setBzsAvailInReal(long bzst, long v) { + nativeFunctions.bz_set_bzs_avail_in_real(bzst, v); + } + + public long getOutputBufferSize(long bzst) { + return nativeFunctions.bz_get_output_buffer_size(bzst); + } + + public void getOutputBuffer(long bzst, byte[] dest) { + if (dest.length == 0) { + return; + } + long nativeDest = NativeMemory.mallocByteArray(dest.length); + try { + nativeFunctions.bz_get_output_buffer(bzst, nativeDest); + NativeMemory.readByteArrayElements(nativeDest, 0, dest, 0, dest.length); + } finally { + NativeMemory.free(nativeDest); + } + } + + public int compressInit(long bzst, int compresslevel) { + return nativeFunctions.bz_compressor_init(bzst, compresslevel); + } + + public int compress(long bzst, byte[] data, long len, int action, long bufsize) { + long nativeData = copyToNativeByteArray(data, (int) len); + try { + return nativeFunctions.bz_compress(bzst, nativeData, len, action, bufsize); + } finally { + if (nativeData != NativeMemory.NULLPTR) { + NativeMemory.free(nativeData); + } + } + } + + public int decompressInit(long bzst) { + return nativeFunctions.bz_decompress_init(bzst); + } + + public int decompress(long bzst, byte[] inputBuffer, long offset, long maxLength, long bufsize, long bzsAvailInReal) { + long nativeInputBuffer = copyToNativeByteArray(inputBuffer); + try { + return nativeFunctions.bz_decompress(bzst, nativeInputBuffer, offset, maxLength, bufsize, bzsAvailInReal); + } finally { + if (nativeInputBuffer != NativeMemory.NULLPTR) { + NativeMemory.free(nativeInputBuffer); + } + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeCompressionSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeCompressionSupport.java new file mode 100644 index 0000000000..ce11c2b994 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeCompressionSupport.java @@ -0,0 +1,106 @@ +/* + * 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; + +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.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleFile; + +abstract class NativeCompressionSupport { + protected final PythonContext pythonContext; + + protected boolean available; + + NativeCompressionSupport(PythonContext context) { + if (context.useNativeCompressionModules()) { + this.pythonContext = context; + this.available = true; + } else { + this.pythonContext = null; + this.available = false; + } + } + + public void setNotAvailable() { + CompilerAsserts.neverPartOfCompilation(); + available = false; + } + + public boolean isAvailable() { + assert !available || PythonContext.get(null).isNativeAccessAllowed(); + return available; + } + + protected static long copyToNativeByteArray(byte[] src, int count) { + if (count <= 0) { + return NativeMemory.NULLPTR; + } + return NativeMemory.copyToNativeByteArray(src, 0, count); + } + + protected static long copyToNativeByteArray(byte[] src) { + return copyToNativeByteArray(src, src.length); + } + + protected static long copyToNativeLongArray(long[] src) { + return src.length == 0 ? NativeMemory.NULLPTR : NativeMemory.copyToNativeLongArray(src); + } + + protected static long copyToNativeIntArray(int[] src) { + return src.length == 0 ? NativeMemory.NULLPTR : NativeMemory.copyToNativeIntArray(src); + } + + @TruffleBoundary + static NativeLibrary loadNativeLibrary(PythonContext context, String name) { + String libName = PythonContext.getSupportLibName(name); + TruffleFile homePath = context.getEnv().getInternalTruffleFile(context.getCAPIHome().toJavaStringUncached()); + TruffleFile file = homePath.resolve(libName); + String path = file.getPath(); + try { + return context.ensureNativeContext().loadLibrary(path, PosixConstants.RTLD_LOCAL.value); + } catch (NativeLibraryLoadException e) { + throw new UnsupportedOperationException(String.format("Could not load compression support library from path '%s'.", path), e); + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeLZMASupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeLZMASupport.java new file mode 100644 index 0000000000..116d90c42f --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeLZMASupport.java @@ -0,0 +1,450 @@ +/* + * 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 + * + * 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; + +import static com.oracle.graal.python.annotations.NativeSimpleType.DOUBLE; +import static com.oracle.graal.python.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT32; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT64; +import static com.oracle.graal.python.annotations.NativeSimpleType.VOID; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.annotations.DowncallSignature; +import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; +import com.oracle.truffle.api.ThreadLocalAction.Access; +import com.oracle.truffle.api.TruffleLogger; + +public final class NativeLZMASupport extends NativeCompressionSupport { + + private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NativeLZMASupport.class); + private static final String SUPPORTING_NATIVE_LIB_NAME = "lzmasupport"; + + public static final int FORMAT_AUTO_INDEX = 0; + public static final int FORMAT_XZ_INDEX = 1; + public static final int FORMAT_ALONE_INDEX = 2; + public static final int FORMAT_RAW_INDEX = 3; + public static final int CHECK_NONE_INDEX = 0; + public static final int CHECK_CRC32_INDEX = 1; + public static final int CHECK_CRC64_INDEX = 2; + public static final int CHECK_SHA256_INDEX = 3; + public static final int CHECK_ID_MAX_INDEX = 4; + public static final int CHECK_UNKNOWN_INDEX = 5; + public static final int FILTER_LZMA1_INDEX = 0; + public static final int FILTER_LZMA2_INDEX = 1; + public static final int FILTER_DELTA_INDEX = 2; + public static final int FILTER_X86_INDEX = 3; + public static final int FILTER_POWERPC_INDEX = 4; + public static final int FILTER_IA64_INDEX = 5; + public static final int FILTER_ARM_INDEX = 6; + public static final int FILTER_ARMTHUMB_INDEX = 7; + public static final int FILTER_SPARC_INDEX = 8; + public static final int MF_HC3_INDEX = 0; + public static final int MF_HC4_INDEX = 1; + public static final int MF_BT2_INDEX = 2; + public static final int MF_BT3_INDEX = 3; + public static final int MF_BT4_INDEX = 4; + public static final int MODE_FAST_INDEX = 0; + public static final int MODE_NORMAL_INDEX = 1; + public static final int PRESET_DEFAULT_INDEX = 0; + public static final int PRESET_EXTREME_INDEX = 1; + public static final int ID_INDEX = 0; + public static final int PRESET_INDEX = 1; + public static final int DICT_SIZE_INDEX = 2; + public static final int LC_INDEX = 3; + public static final int LP_INDEX = 4; + public static final int PB_INDEX = 5; + public static final int MODE_INDEX = 6; + public static final int NICE_LEN_INDEX = 7; + public static final int MF_INDEX = 8; + public static final int DEPTH_INDEX = 9; + public static final int DIST_INDEX = 1; + public static final int START_OFFSET_INDEX = 1; + public static final int MAX_OPTS_INDEX = 10; + public static final int LZMA_ID_ERROR = 98; + public static final int LZMA_PRESET_ERROR = 99; + + abstract static class LZMANativeFunctions { + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER, POINTER, POINTER, POINTER, POINTER, POINTER}) + abstract void get_macros(long formats, long checks, long filters, long mfs, long modes, long preset); + + @DowncallSignature(returnType = POINTER) + abstract long lzma_create_lzmast_stream(); + + @DowncallSignature(returnType = DOUBLE, argumentTypes = {POINTER}) + abstract double lzma_get_timeElapsed(long lzmast); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER}) + abstract void lzma_free_stream(long lzmast); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER}) + abstract void lzma_gc_helper(long lzmast); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long lzma_get_next_in_index(long lzmast); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long lzma_get_lzs_avail_in(long lzmast); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long lzma_get_lzs_avail_out(long lzmast); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int lzma_lzma_get_check(long lzmast); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER, SINT64}) + abstract void lzma_set_lzs_avail_in(long lzmast, long v); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long lzma_get_output_buffer_size(long lzmast); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER, POINTER}) + abstract void lzma_get_output_buffer(long lzmast, long dest); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int lzma_lzma_check_is_supported(int checkId); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, POINTER}) + abstract int lzma_set_filter_spec_lzma(long lzmast, int fidx, long opts); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, POINTER}) + abstract int lzma_set_filter_spec_delta(long lzmast, int fidx, long opts); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, POINTER}) + abstract int lzma_set_filter_spec_bcj(long lzmast, int fidx, long opts); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int lzma_encode_filter_spec(long lzmast, long opts); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER, SINT32, POINTER}) + abstract int lzma_decode_filter_spec(long filterId, long encodedProps, int len, long opts); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, SINT32}) + abstract int lzma_lzma_easy_encoder(long lzmast, int preset, int check); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32}) + abstract int lzma_lzma_stream_encoder(long lzmast, int check); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32}) + abstract int lzma_lzma_alone_encoder_preset(long lzmast, int preset); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int lzma_lzma_alone_encoder(long lzmast); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int lzma_lzma_raw_encoder(long lzmast); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT32, SINT64}) + abstract int lzma_compress(long lzmast, long data, long len, int iaction, long bufsize); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int lzma_lzma_raw_decoder(long lzmast); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64, SINT32}) + abstract int lzma_lzma_auto_decoder(long lzmast, long memlimit, int decoderFlags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64, SINT32}) + abstract int lzma_lzma_stream_decoder(long lzmast, long memlimit, int decoderFlags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int lzma_lzma_alone_decoder(long lzmast, long memlimit); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT64, SINT64, SINT64}) + abstract int lzma_decompress(long lzmast, long inputBuffer, long offset, long maxLength, long bufsize, long lzsAvailIn); + + static NativeLibrary loadNativeLibrary(PythonContext context) { + return NativeCompressionSupport.loadNativeLibrary(context, SUPPORTING_NATIVE_LIB_NAME); + } + } + + private final LZMANativeFunctions nativeFunctions; + + private NativeLZMASupport(PythonContext context) { + super(context); + this.nativeFunctions = isAvailable() ? new LZMANativeFunctionsGen(context) : null; + } + + public static NativeLZMASupport createNative(PythonContext context, String noNativeAccessHelp) { + return new NativeLZMASupport(context); + } + + static class PointerReleaseCallback implements AsyncHandler.AsyncAction { + private final Pointer pointer; + + public PointerReleaseCallback(Pointer pointer) { + this.pointer = pointer; + } + + @Override + public void execute(PythonContext context, Access access) { + if (!pointer.markReleased()) { + assert pointer.isReleased(); + return; + } + try { + pointer.doRelease(); + LOGGER.finest("NativeLZMASupport pointer has been freed"); + } catch (Exception e) { + LOGGER.severe("Error while trying to free NativeLZMASupport pointer: " + e.getMessage()); + } + } + } + + public static class Pointer extends AsyncHandler.SharedFinalizer.FinalizableReference { + + private final NativeLZMASupport lib; + private final long pointer; + + public Pointer(Object referent, long pointer, NativeLZMASupport lib) { + super(referent, lib.pythonContext.getSharedFinalizer()); + this.lib = lib; + this.pointer = pointer; + } + + public long getPointer() { + return pointer; + } + + protected void doRelease() { + lib.gcReleaseHelper(pointer); + } + + @Override + public AsyncHandler.AsyncAction release() { + if (!isReleased()) { + return new PointerReleaseCallback(this); + } + return null; + } + } + + public void getMacros(int[] formats, int[] checks, long[] filters, int[] mfs, int[] modes, long[] preset) { + long nativeFormats = copyToNativeIntArray(formats); + long nativeChecks = copyToNativeIntArray(checks); + long nativeFilters = copyToNativeLongArray(filters); + long nativeMfs = copyToNativeIntArray(mfs); + long nativeModes = copyToNativeIntArray(modes); + long nativePreset = copyToNativeLongArray(preset); + try { + nativeFunctions.get_macros(nativeFormats, nativeChecks, nativeFilters, nativeMfs, nativeModes, nativePreset); + NativeMemory.readIntArrayElements(nativeFormats, 0, formats, 0, formats.length); + NativeMemory.readIntArrayElements(nativeChecks, 0, checks, 0, checks.length); + NativeMemory.readLongArrayElements(nativeFilters, 0, filters, 0, filters.length); + NativeMemory.readIntArrayElements(nativeMfs, 0, mfs, 0, mfs.length); + NativeMemory.readIntArrayElements(nativeModes, 0, modes, 0, modes.length); + NativeMemory.readLongArrayElements(nativePreset, 0, preset, 0, preset.length); + } finally { + NativeMemory.free(nativeFormats); + NativeMemory.free(nativeChecks); + NativeMemory.free(nativeFilters); + NativeMemory.free(nativeMfs); + NativeMemory.free(nativeModes); + NativeMemory.free(nativePreset); + } + } + + public Object getTimeElapsed(long lzmast) { + return nativeFunctions.lzma_get_timeElapsed(lzmast); + } + + public Object gcReleaseHelper(long lzmast) { + nativeFunctions.lzma_gc_helper(lzmast); + return null; + } + + public long createStream() { + return nativeFunctions.lzma_create_lzmast_stream(); + } + + public void deallocateStream(long lzmast) { + nativeFunctions.lzma_free_stream(lzmast); + } + + public long getNextInIndex(long lzmast) { + return nativeFunctions.lzma_get_next_in_index(lzmast); + } + + public long getLzsAvailIn(long lzmast) { + return nativeFunctions.lzma_get_lzs_avail_in(lzmast); + } + + public long getLzsAvailOut(long lzmast) { + return nativeFunctions.lzma_get_lzs_avail_out(lzmast); + } + + public int getLzsCheck(long lzmast) { + return nativeFunctions.lzma_lzma_get_check(lzmast); + } + + public void setLzsAvailIn(long lzmast, long v) { + nativeFunctions.lzma_set_lzs_avail_in(lzmast, v); + } + + public long getOutputBufferSize(long lzmast) { + return nativeFunctions.lzma_get_output_buffer_size(lzmast); + } + + public void getOutputBuffer(long lzmast, byte[] dest) { + if (dest.length == 0) { + return; + } + long nativeDest = NativeMemory.mallocByteArray(dest.length); + try { + nativeFunctions.lzma_get_output_buffer(lzmast, nativeDest); + NativeMemory.readByteArrayElements(nativeDest, 0, dest, 0, dest.length); + } finally { + NativeMemory.free(nativeDest); + } + } + + public int checkIsSupported(int checkId) { + return nativeFunctions.lzma_lzma_check_is_supported(checkId); + } + + public int setFilterSpecLZMA(long lzmast, int fidx, long[] opts) { + long nativeOpts = copyToNativeLongArray(opts); + try { + return nativeFunctions.lzma_set_filter_spec_lzma(lzmast, fidx, nativeOpts); + } finally { + NativeMemory.free(nativeOpts); + } + } + + public int setFilterSpecDelta(long lzmast, int fidx, long[] opts) { + long nativeOpts = copyToNativeLongArray(opts); + try { + return nativeFunctions.lzma_set_filter_spec_delta(lzmast, fidx, nativeOpts); + } finally { + NativeMemory.free(nativeOpts); + } + } + + public int setFilterSpecBCJ(long lzmast, int fidx, long[] opts) { + long nativeOpts = copyToNativeLongArray(opts); + try { + return nativeFunctions.lzma_set_filter_spec_bcj(lzmast, fidx, nativeOpts); + } finally { + NativeMemory.free(nativeOpts); + } + } + + public int encodeFilter(long lzmast, long[] opts) { + long nativeOpts = copyToNativeLongArray(opts); + try { + return nativeFunctions.lzma_encode_filter_spec(lzmast, nativeOpts); + } finally { + NativeMemory.free(nativeOpts); + } + } + + public int decodeFilter(long filterId, byte[] encodedProps, int len, long[] opts) { + long nativeEncodedProps = copyToNativeByteArray(encodedProps, len); + long nativeOpts = copyToNativeLongArray(opts); + try { + int result = nativeFunctions.lzma_decode_filter_spec(filterId, nativeEncodedProps, len, nativeOpts); + NativeMemory.readLongArrayElements(nativeOpts, 0, opts, 0, opts.length); + return result; + } finally { + if (nativeEncodedProps != NativeMemory.NULLPTR) { + NativeMemory.free(nativeEncodedProps); + } + NativeMemory.free(nativeOpts); + } + } + + public int lzmaEasyEncoder(long lzmast, long preset, int check) { + return nativeFunctions.lzma_lzma_easy_encoder(lzmast, (int) preset, check); + } + + public int lzmaStreamEncoder(long lzmast, int check) { + return nativeFunctions.lzma_lzma_stream_encoder(lzmast, check); + } + + public int lzmaAloneEncoderPreset(long lzmast, long preset) { + return nativeFunctions.lzma_lzma_alone_encoder_preset(lzmast, (int) preset); + } + + public int lzmaAloneEncoder(long lzmast) { + return nativeFunctions.lzma_lzma_alone_encoder(lzmast); + } + + public int lzmaRawEncoder(long lzmast) { + return nativeFunctions.lzma_lzma_raw_encoder(lzmast); + } + + public int compress(long lzmast, byte[] data, long len, int iaction, long bufsize) { + long nativeData = copyToNativeByteArray(data, (int) len); + try { + return nativeFunctions.lzma_compress(lzmast, nativeData, len, iaction, bufsize); + } finally { + if (nativeData != NativeMemory.NULLPTR) { + NativeMemory.free(nativeData); + } + } + } + + public int lzmaRawDecoder(long lzmast) { + return nativeFunctions.lzma_lzma_raw_decoder(lzmast); + } + + public int lzmaAutoDecoder(long lzmast, long memlimit, long decoderFlags) { + return nativeFunctions.lzma_lzma_auto_decoder(lzmast, memlimit, (int) decoderFlags); + } + + public int lzmaStreamDecoder(long lzmast, long memlimit, long decoderFlags) { + return nativeFunctions.lzma_lzma_stream_decoder(lzmast, memlimit, (int) decoderFlags); + } + + public int lzmaAloneDecoder(long lzmast, long memlimit) { + return nativeFunctions.lzma_lzma_alone_decoder(lzmast, memlimit); + } + + public int decompress(long lzmast, byte[] inputBuffer, long offset, long maxLength, long bufsize, long lzsAvailIn) { + long nativeInputBuffer = copyToNativeByteArray(inputBuffer); + try { + return nativeFunctions.lzma_decompress(lzmast, nativeInputBuffer, offset, maxLength, bufsize, lzsAvailIn); + } finally { + if (nativeInputBuffer != NativeMemory.NULLPTR) { + NativeMemory.free(nativeInputBuffer); + } + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeLibrary.java deleted file mode 100644 index 63468877f7..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeLibrary.java +++ /dev/null @@ -1,425 +0,0 @@ -/* - * 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.runtime; - -import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; -import static com.oracle.graal.python.util.PythonUtils.callCallTarget; - -import java.lang.invoke.VarHandle; -import java.util.Objects; -import java.util.logging.Level; - -import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.runtime.NativeLibraryFactory.InvokeNativeFunctionNodeGen; -import com.oracle.graal.python.util.FunctionWithSignature; -import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.TruffleFile; -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.NeverDefault; -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.UnknownIdentifierException; -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.Node; -import com.oracle.truffle.api.profiles.InlinedExactClassProfile; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.strings.TruffleString; -import com.oracle.truffle.nfi.api.SignatureLibrary; - -/** - * Wraps a native library loaded via NFI and provides caching for functions looked up in the - * library. The set of functions to be loaded from the library is expressed as Java enum - * implementing {@link NativeFunction}. This is runtime object: it should not be cached in the AST - * and is expected to be stored in the context. - *

      - * Because of Truffle DSL restrictions this class cannot be generic, but users should work with - * generic subclass {@link TypedNativeLibrary}, which can be created with one of the {@code create} - * factory methods. - *

      - * For now, until there is no need to access the library and function objects directly, this object - * is opaque to the outside code and the only entrypoint is {@link InvokeNativeFunction}, which - * lazily loads the library, the requested function and invokes it. This node takes care of - * efficient caching of the loaded NFI objects. - */ -public class NativeLibrary { - private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NativeLibrary.class); - - /** - * This interface is intended to be implemented by enums. - */ - interface NativeFunction { - String signature(); - - String name(); - - int ordinal(); - } - - enum NFIBackend { - NATIVE(""), - LLVM("with llvm "); - - private final String withClause; - - NFIBackend(String withClause) { - this.withClause = withClause; - } - } - - /** - * This is a helper exception that will be thrown in case a library is {@link #optional} and not - * available. - */ - public static final class NativeLibraryCannotBeLoaded extends RuntimeException { - private static final NativeLibraryCannotBeLoaded INSTANCE = new NativeLibraryCannotBeLoaded(); - private static final long serialVersionUID = 6066722947025284374L; - - private NativeLibraryCannotBeLoaded() { - super(null, null); - } - - @SuppressWarnings("sync-override") - @Override - public Throwable fillInStackTrace() { - return this; - } - } - - private final int functionsCount; - private final String name; - private final NFIBackend nfiBackend; - - /** - * If given functionality has a fully managed variant that can be configured, this help message - * should explain how to switch to it. It will be printed if loading of the native library - * fails. - */ - private final String noNativeAccessHelp; - private final boolean optional; - - private volatile FunctionWithSignature[] cachedFunctions; - private volatile Object cachedLibrary; - private volatile InteropLibrary cachedLibraryInterop; - - public NativeLibrary(String name, int functionsCount, NFIBackend nfiBackend, String noNativeAccessHelp, boolean optional) { - this.functionsCount = functionsCount; - this.name = name; - this.nfiBackend = nfiBackend; - this.noNativeAccessHelp = noNativeAccessHelp; - this.optional = optional; - } - - private Object getCachedLibrary(Node location, PythonContext context) { - if (cachedLibrary == null) { - // This should be a one-off thing for each context - CompilerDirectives.transferToInterpreter(); - synchronized (this) { - if (cachedLibrary == null) { - Object lib = loadLibrary(location, context); - if (lib != null) { - // order matters due to multi-threading cases. - cachedLibraryInterop = InteropLibrary.getUncached(lib); - cachedLibrary = lib; - } - } - } - } - return cachedLibrary; - } - - private FunctionWithSignature getCachedFunction(Node location, PythonContext context, NativeFunction function) { - Object lib = getCachedLibrary(location, context); - if (cachedFunctions == null) { - // This should be a one-off thing for each context - CompilerDirectives.transferToInterpreter(); - synchronized (this) { - if (cachedFunctions == null) { - cachedFunctions = new FunctionWithSignature[functionsCount]; - } - } - } - int functionIndex = function.ordinal(); - if (cachedFunctions[functionIndex] == null) { - // This should be a one-off thing for each context - CompilerDirectives.transferToInterpreter(); - synchronized (this) { - FunctionWithSignature signature = getFunction(location, context, lib, function); - VarHandle.storeStoreFence(); - // it is OK to overwrite cachedFunctions[functionIndex] that may have been - // written from another thread: no need to double-check that it's still null. - // dummy is volatile, the object must be fully initialized at this point - cachedFunctions[functionIndex] = signature; - } - } - return cachedFunctions[functionIndex]; - } - - private FunctionWithSignature getFunction(Node location, PythonContext context, NativeFunction function) { - CompilerAsserts.neverPartOfCompilation(); - Object lib = getCachedLibrary(location, context); - return getFunction(location, context, lib, function); - } - - private Object parseSignature(Node location, PythonContext context, String signature) { - Source sigSource = Source.newBuilder(J_NFI_LANGUAGE, nfiBackend.withClause + signature, "python-nfi-signature").build(); - return callCallTarget(context.getEnv().parseInternal(sigSource), location); - } - - private FunctionWithSignature getFunction(Node location, PythonContext context, Object lib, NativeFunction function) { - CompilerAsserts.neverPartOfCompilation(); - try { - Object signature = parseSignature(location, context, function.signature()); - Object symbol = cachedLibraryInterop.readMember(lib, function.name()); - return new FunctionWithSignature(signature, symbol); - } catch (UnsupportedMessageException | UnknownIdentifierException e) { - throw new IllegalStateException(String.format("Cannot load symbol '%s' from the internal shared library '%s'", function.name(), name), e); - } - } - - private Object loadLibrary(Node location, PythonContext context) { - CompilerAsserts.neverPartOfCompilation(); - if (context.isNativeAccessAllowed()) { - String path = getLibPath(context, name); - String src = String.format("%sload (RTLD_LOCAL) \"%s\"", nfiBackend.withClause, path); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine(String.format("Loading native library %s from path %s %s", name, path, nfiBackend.withClause)); - } - Source loadSrc = Source.newBuilder(J_NFI_LANGUAGE, src, "load:" + name).internal(true).build(); - try { - return context.getEnv().parseInternal(loadSrc).call(location); - } catch (RuntimeException ex) { - Level level = optional ? Level.FINE : Level.SEVERE; - if (LOGGER.isLoggable(level)) { - LOGGER.log(level, ex, () -> String.format("Error while opening shared library at '%s'.\nFull NFI source: %s.", path, src)); - } - if (!optional) { - throw new RuntimeException(String.format( - "Cannot load supporting native library '%s'. " + - "Either the shared library file does not exist, or your system may be missing some dependencies. " + - "Turn on logging with --log.%s.level=INFO for more details. %s", - name, - NativeLibrary.class.getName(), - noNativeAccessHelp)); - } - } - } else { - throw new RuntimeException(String.format( - "Cannot load supporting native library '%s' because the native access is not allowed. " + - "The native access should be allowed when running GraalPython via the graalpython command. " + - "If you are embedding GraalPy using the Context API, make sure to allow native access using 'allowNativeAccess(true)'. %s", - name, - noNativeAccessHelp)); - } - throw NativeLibraryCannotBeLoaded.INSTANCE; - } - - private static String getLibPath(PythonContext context, String name) { - CompilerAsserts.neverPartOfCompilation(); - TruffleFile homePath = context.getEnv().getInternalTruffleFile(context.getCAPIHome().toJavaStringUncached()); - TruffleFile file = homePath.resolve(name); - return file.getPath(); - } - - protected Object callUncached(PythonContext context, NativeFunction f, Object... args) { - CompilerAsserts.neverPartOfCompilation(); - final Object lib = getCachedLibrary(null, context); - if (lib != null) { - try { - Object signature = parseSignature(null, context, f.signature()); - Object symbol = cachedLibraryInterop.readMember(lib, f.name()); - return SignatureLibrary.getUncached().call(signature, symbol, args); - } catch (Exception e) { - throw CompilerDirectives.shouldNotReachHere(f.name(), e); - } - } - return null; - } - - public static & NativeFunction> TypedNativeLibrary create(String name, T[] functions, String noNativeAccessHelp, boolean canIgnore) { - return create(name, functions, NFIBackend.NATIVE, noNativeAccessHelp, canIgnore); - } - - public static & NativeFunction> TypedNativeLibrary create(String name, T[] functions, NFIBackend nfiBackendName, String noNativeAccessHelp, boolean canIgnore) { - return new TypedNativeLibrary<>(name, functions.length, nfiBackendName, noNativeAccessHelp, canIgnore); - } - - public static final class TypedNativeLibrary & NativeFunction> extends NativeLibrary { - public TypedNativeLibrary(String name, int functionsCount, NFIBackend nfiBackendName, String noNativeAccessHelp, boolean canIgnore) { - super(name, functionsCount, nfiBackendName, noNativeAccessHelp, canIgnore); - } - } - - public abstract static class InvokeNativeFunction extends PNodeWithContext { - private static final InvokeNativeFunction UNCACHED = InvokeNativeFunctionNodeGen.create(InteropLibrary.getUncached()); - @Child private InteropLibrary resultInterop; - @Child private TruffleString.SwitchEncodingNode switchEncodingNode; - - public InvokeNativeFunction(InteropLibrary resultInterop) { - this.resultInterop = resultInterop; - } - - @NeverDefault - public static InvokeNativeFunction create() { - return InvokeNativeFunctionNodeGen.create(null); - } - - public static InvokeNativeFunction getUncached() { - return UNCACHED; - } - - public & NativeFunction> Object call(TypedNativeLibrary lib, T function, Object... args) { - return execute(lib, function, args); - } - - public & NativeFunction> long callLong(TypedNativeLibrary lib, T function, Object... args) { - try { - return ensureResultInterop().asLong(call(lib, function, args)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(function.name(), e); - } - } - - public & NativeFunction> int callInt(TypedNativeLibrary lib, T function, Object... args) { - try { - return ensureResultInterop().asInt(call(lib, function, args)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(function.name(), e); - } - } - - public & NativeFunction> TruffleString callString(TypedNativeLibrary lib, T function, Object... args) { - try { - return ensureSwitchEncoding().execute(ensureResultInterop().asTruffleString(call(lib, function, args)), TS_ENCODING); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(function.name(), e); - } - } - - protected abstract Object execute(NativeLibrary lib, NativeFunction function, Object[] args); - - @Specialization(guards = {"isSingleContext()", "function == cachedFunction", "lib == cachedLib"}, limit = "3") - static Object doSingleContext(@SuppressWarnings("unused") NativeLibrary lib, @SuppressWarnings("unused") NativeFunction function, Object[] args, - @SuppressWarnings("unused") @Cached(value = "lib", weak = true) NativeLibrary cachedLib, - @Cached("function") NativeFunction cachedFunction, - @Cached(value = "getFunction($node, lib, function)", weak = true) FunctionWithSignature funObj, - @CachedLibrary("funObj.signature()") SignatureLibrary funInterop) { - return invoke(cachedFunction, args, funObj, funInterop); - } - - @Specialization(replaces = "doSingleContext") - static Object doMultiContext(NativeLibrary lib, NativeFunction functionIn, Object[] args, - @Bind Node inliningTarget, - @Cached InlinedExactClassProfile functionClassProfile, - @CachedLibrary(limit = "1") SignatureLibrary funInterop) { - NativeFunction function = functionClassProfile.profile(inliningTarget, functionIn); - FunctionWithSignature funObj = lib.getCachedFunction(inliningTarget, PythonContext.get(funInterop), function); - return invoke(function, args, funObj, funInterop); - } - - private static Object invoke(NativeFunction function, Object[] args, FunctionWithSignature funObj, SignatureLibrary funInterop) { - try { - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest(buildLogMessage(function, args)); - } - Object result = funInterop.call(funObj.signature(), funObj.function(), args); - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest(buildReturnLogMessage(function, result)); - } - return result; - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(function.name(), e); - } - } - - protected FunctionWithSignature getFunction(Node location, NativeLibrary lib, NativeFunction fun) { - return lib.getFunction(location, PythonContext.get(this), fun); - } - - @TruffleBoundary - private static String buildLogMessage(NativeFunction function, Object[] args) { - StringBuilder sb = new StringBuilder("Executing native function "); - sb.append(function.name()).append(" with arguments: "); - for (Object arg : args) { - sb.append(safeToString(arg)).append(','); - } - return sb.toString(); - } - - @TruffleBoundary - private static String buildReturnLogMessage(NativeFunction function, Object result) { - return "Finished executing native function " + function.name() + " with result: " + safeToString(result); - } - - private static String safeToString(Object value) { - try { - return Objects.toString(value); - } catch (Exception ex) { - return String.format("%s (toString threw %s),", value.getClass().getSimpleName(), ex.getClass().getSimpleName()); - } - } - - public InteropLibrary ensureResultInterop() { - if (resultInterop == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - resultInterop = insert(InteropLibrary.getFactory().createDispatched(3)); - } - return resultInterop; - } - - public TruffleString.SwitchEncodingNode ensureSwitchEncoding() { - if (switchEncodingNode == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - switchEncodingNode = insert(TruffleString.SwitchEncodingNode.create()); - } - return switchEncodingNode; - } - - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixConstants.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java similarity index 96% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixConstants.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java index ef20d53718..edb4fb39df 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixConstants.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 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,7 +40,7 @@ */ package com.oracle.graal.python.runtime; -public enum NFIPosixConstants { +public enum NativePosixConstants { // start generated SIZEOF_STRUCT_SOCKADDR, SIZEOF_STRUCT_SOCKADDR_SA_FAMILY, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java new file mode 100644 index 0000000000..3be698bca1 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java @@ -0,0 +1,3424 @@ +/* + * 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. + */ +// skip GIL +package com.oracle.graal.python.runtime; + +import static com.oracle.graal.python.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT32; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT64; +import static com.oracle.graal.python.annotations.NativeSimpleType.VOID; +import static com.oracle.graal.python.lib.PyUnicodeFSDecoderNode.SURROGATE_ESCAPE_TO_UTF8_TRANSCODING_ERROR_HANDLER; +import static com.oracle.graal.python.nodes.StringLiterals.T_NATIVE; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_IN_ADDR_S_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_IN; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_IN6; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_SA_FAMILY; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_STORAGE; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_UN_SUN_PATH; +import static com.oracle.graal.python.runtime.PosixConstants.AF_INET; +import static com.oracle.graal.python.runtime.PosixConstants.AF_INET6; +import static com.oracle.graal.python.runtime.PosixConstants.AF_UNIX; +import static com.oracle.graal.python.runtime.PosixConstants.AF_UNSPEC; +import static com.oracle.graal.python.runtime.PosixConstants.HOST_NAME_MAX; +import static com.oracle.graal.python.runtime.PosixConstants.INET6_ADDRSTRLEN; +import static com.oracle.graal.python.runtime.PosixConstants.INET_ADDRSTRLEN; +import static com.oracle.graal.python.runtime.PosixConstants.L_ctermid; +import static com.oracle.graal.python.runtime.PosixConstants.NI_MAXHOST; +import static com.oracle.graal.python.runtime.PosixConstants.NI_MAXSERV; +import static com.oracle.graal.python.runtime.PosixConstants.PATH_MAX; +import static com.oracle.graal.python.runtime.PosixConstants.WNOHANG; +import static com.oracle.graal.python.runtime.PosixConstants._POSIX_HOST_NAME_MAX; +import static com.oracle.graal.python.runtime.PosixSupportLibrary.POSIX_FILENAME_SEPARATOR; +import static com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; +import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR; +import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR_BE; +import static com.oracle.graal.python.util.PythonUtils.EMPTY_LONG_ARRAY; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; +import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_8; + +import java.util.ArrayList; +import java.util.logging.Level; + +import org.graalvm.nativeimage.ImageInfo; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.annotations.DowncallSignature; +import com.oracle.graal.python.annotations.PythonOS; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; +import com.oracle.graal.python.lib.PyUnicodeFSDecoderNode; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.runtime.PosixSupportLibrary.AcceptResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursor; +import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursorLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Buffer; +import com.oracle.graal.python.runtime.PosixSupportLibrary.GetAddrInfoException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet4SockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet6SockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidUnixSocketPathException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.OpenPtyResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.RusageResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddrLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; +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.util.OverflowException; +import com.oracle.graal.python.util.PythonUtils; +import com.oracle.truffle.api.ArrayUtils; +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.TruffleFile; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.TruffleLogger; +import com.oracle.truffle.api.TruffleSafepoint; +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.Specialization; +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 sun.misc.Unsafe; + +/** + * Implementation that invokes the native POSIX support library through generated native-access + * downcalls. + */ +@ExportLibrary(PosixSupportLibrary.class) +public final class NativePosixSupport extends PosixSupport { + private static final String SUPPORTING_NATIVE_LIB_NAME = "posix"; + private static final int UNAME_BUF_LENGTH = 256; + private static final int DIRENT_NAME_BUF_LENGTH = 256; + private static final int PWD_OUTPUT_LEN = 5; + private static final int PWD_BUFFER_MAX_SIZE = Integer.MAX_VALUE >> 2; + private static final int STRERROR_BUF_LENGTH = 1024; + + private static final int MAX_READ = Integer.MAX_VALUE / 2; + + private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NativePosixSupport.class); + + private static final Unsafe UNSAFE = PythonUtils.initUnsafe(); + + private static final Object CRYPT_LOCK = new Object(); + + abstract static class PosixNativeFunctionInvoker { + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32}) + abstract int init_constants(long out, int len); + + @DowncallSignature(returnType = SINT32) + abstract int get_errno(); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT32}) + abstract void set_errno(int errno); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64, SINT32, SINT32, SINT32, SINT64}) + abstract long call_mmap(long length, int prot, int flags, int fd, long offset); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, SINT64}) + abstract int call_munmap(long address, long length); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT64, SINT64, SINT64}) + abstract void call_msync(long address, long offset, long length); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT32, POINTER, SINT32}) + abstract void call_strerror(int error, long buf, int buflen); + + @DowncallSignature(returnType = SINT64) + abstract long call_getpid(); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_umask(int mask); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32}) + abstract int call_openat(int dirFd, long pathname, int flags, int mode); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_close(int fd); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, POINTER, SINT64}) + abstract long call_read(int fd, long buf, long count); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, POINTER, SINT64}) + abstract long call_write(int fd, long buf, long count); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_dup(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32}) + abstract int call_dup2(int oldfd, int newfd, int inheritable); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_pipe2(long pipefd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER, SINT32, POINTER, SINT32, SINT64, SINT64, POINTER}) + abstract int call_select(int nfds, long readfds, int readfdsLen, long writefds, int writefdsLen, long errfds, int errfdsLen, long timeoutSec, long timeoutUsec, long selected); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT64, SINT64}) + abstract int call_poll(int fd, int writing, long timeoutSec, long timeoutUsec); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, SINT64, SINT32}) + abstract long call_lseek(int fd, long offset, int whence); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT64}) + abstract int call_ftruncate(int fd, long length); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int call_truncate(long path, long length); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_fsync(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int call_flock(int fd, int operation); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32, SINT32, SINT64, SINT64}) + abstract int call_fcntl_lock(int fd, int blocking, int lockType, int whence, long start, long length); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER}) + abstract int call_fstatat(int dirFd, long path, int followSymlinks, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_fstat(int fd, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_statvfs(long path, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_fstatvfs(int fd, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, POINTER, POINTER, POINTER, SINT32}) + abstract int call_uname(long sysname, long nodename, long release, long version, long machine, int size); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) + abstract int call_unlinkat(int dirFd, long pathname, int rmdir); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER, SINT32}) + abstract int call_linkat(int oldDirFd, long oldPath, int newDirFd, long newPath, int flags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, POINTER}) + abstract int call_symlinkat(long target, int dirFd, long linkpath); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) + abstract int call_mkdirat(int dirFd, long pathname, int mode); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int call_getcwd(long buf, long size); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_chdir(long path); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_fchdir(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_isatty(int fd); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long call_opendir(long name); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32}) + abstract long call_fdopendir(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64}) + abstract int call_closedir(long dirp); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER, SINT64, POINTER}) + abstract int call_readdir(long dirp, long nameBuf, long nameBufSize, long out); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT64}) + abstract void call_rewinddir(long dirp); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER, SINT32}) + abstract int call_utimensat(int dirFd, long path, long timespec, int followSymlinks); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_futimens(int fd, long timespec); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_futimes(int fd, long timeval); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_lutimes(long filename, long timeval); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_utimes(long filename, long timeval); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER}) + abstract int call_renameat(int oldDirFd, long oldPath, int newDirFd, long newPath); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32, SINT32}) + abstract int call_faccessat(int dirFd, long path, int mode, int effectiveIds, int followSymlinks); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32}) + abstract int call_fchmodat(int dirFd, long path, int mode, int followSymlinks); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int call_fchmod(int fd, int mode); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT64, SINT64, SINT32}) + abstract int call_fchownat(int dirfd, long pathname, long owner, long group, int followSymlinks); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT64, SINT64}) + abstract int call_fchown(int fd, long owner, long group); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, POINTER, POINTER, SINT64}) + abstract long call_readlinkat(int dirFd, long path, long buf, long size); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int get_inheritable(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int set_inheritable(int fd, int inheritable); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int get_blocking(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int set_blocking(int fd, int blocking); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int get_terminal_size(int fd, long size); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_raise(int signal); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_alarm(int seconds); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_getitimer(int which, long currentValue); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_setitimer(int which, long newValue, long oldValue); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int signal_self(int signal); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, SINT32}) + abstract int call_kill(long pid, int signal); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, SINT32}) + abstract int call_killpg(long pgid, int signal); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64, POINTER, SINT32}) + abstract long call_waitpid(long pid, long status, int options); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wcoredump(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wifcontinued(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wifstopped(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wifsignaled(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wifexited(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wexitstatus(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wtermsig(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wstopsig(int status); + + @DowncallSignature(returnType = SINT64) + abstract long call_getuid(); + + @DowncallSignature(returnType = SINT64) + abstract long call_geteuid(); + + @DowncallSignature(returnType = SINT64) + abstract long call_getgid(); + + @DowncallSignature(returnType = SINT64) + abstract long call_getegid(); + + @DowncallSignature(returnType = SINT64) + abstract long call_getppid(); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64}) + abstract long call_getpgid(long pid); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, SINT64}) + abstract int call_setpgid(long pid, long pgid); + + @DowncallSignature(returnType = SINT64) + abstract long call_getpgrp(); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64}) + abstract long call_getsid(long pid); + + @DowncallSignature(returnType = SINT64) + abstract long call_setsid(); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER}) + abstract int call_getgroups(long size, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_getrusage(int who, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_openpty(long outvars); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_ctermid(long buf); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT32}) + abstract int call_setenv(long name, long value, int overwrite); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_unsetenv(long name); + + @DowncallSignature(returnType = SINT32, argumentTypes = { + POINTER, POINTER, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, + SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, POINTER, SINT64 + }) + abstract int fork_exec(long data, long offsets, int offsetsLen, int argsPos, int envPos, int cwdPos, int stdinRdFd, int stdinWrFd, int stdoutRdFd, int stdoutWrFd, int stderrRdFd, + int stderrWrFd, int errPipeRdFd, int errPipeWrFd, int closeFds, int restoreSignals, int callSetsid, int pgidToSet, int allowVFork, long fdsToKeep, long fdsToKeepLen); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER, POINTER, SINT32}) + abstract void call_execv(long data, long offsets, int offsetsLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_system(long pathname); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER, SINT32, POINTER}) + abstract int call_getpwuid_r(long uid, long buffer, int bufferSize, long output); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT32, POINTER}) + abstract int call_getpwname_r(long name, long buffer, int bufferSize, long output); + + @DowncallSignature(returnType = VOID) + abstract void call_setpwent(); + + @DowncallSignature(returnType = VOID) + abstract void call_endpwent(); + + @DowncallSignature(returnType = POINTER, argumentTypes = {POINTER}) + abstract long call_getpwent(long bufferSize); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT32, POINTER}) + abstract int get_getpwent_data(long p, long buffer, int bufferSize, long output); + + @DowncallSignature(returnType = SINT64) + abstract long get_sysconf_getpw_r_size_max(); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32}) + abstract int call_socket(int family, int type, int protocol); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_accept(int sockfd, long addr, long addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) + abstract int call_bind(int sockfd, long addr, int addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) + abstract int call_connect(int sockfd, long addr, int addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int call_listen(int sockfd, int backlog); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_getpeername(int sockfd, long addr, long addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_getsockname(int sockfd, long addr, long addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32}) + abstract int call_send(int sockfd, long buf, int len, int flags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32, SINT32, POINTER, SINT32}) + abstract int call_sendto(int sockfd, long buf, int offset, int len, int flags, long addr, int addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32}) + abstract int call_recv(int sockfd, long buf, int len, int flags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32, SINT32, POINTER, POINTER}) + abstract int call_recvfrom(int sockfd, long buf, int offset, int len, int flags, long srcAddr, long addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int call_shutdown(int sockfd, int how); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32, POINTER, POINTER}) + abstract int call_getsockopt(int sockfd, int level, int optname, long buf, long bufLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32, POINTER, SINT32}) + abstract int call_setsockopt(int sockfd, int level, int optname, long buf, int bufLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_inet_addr(long src); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long call_inet_aton(long src); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_inet_ntoa(int src, long dst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_inet_pton(int family, long src, long dst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER, SINT32}) + abstract int call_inet_ntop(int family, long src, long dst, int dstSize); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int call_gethostname(long buf, long bufLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, POINTER, SINT32, POINTER, SINT32, SINT32}) + abstract int call_getnameinfo(long addr, int addrLen, long hostBuf, int hostBufLen, long servBuf, int servBufLen, int flags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT32, SINT32, SINT32, SINT32, POINTER}) + abstract int call_getaddrinfo(long node, long service, int family, int sockType, int protocol, int flags, long ptr); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT64}) + abstract void call_freeaddrinfo(long ptr); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT32, POINTER, SINT32}) + abstract void call_gai_strerror(int error, long buf, int buflen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER, POINTER, POINTER}) + abstract int get_addrinfo_members(long ptr, long intData, long longData, long addr); + + @DowncallSignature(returnType = POINTER, argumentTypes = {POINTER, SINT32, SINT32, SINT32}) + abstract long call_sem_open(long name, int openFlags, int mode, int value); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_close(long handle); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_unlink(long name); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_sem_getvalue(long handle, long value); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_post(long handle); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_wait(long handle); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_trywait(long handle); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int call_sem_timedwait(long handle, long deadlineNs); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT64, POINTER}) + abstract int call_ioctl_bytes(int fd, long request, long buffer); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT64, SINT32}) + abstract int call_ioctl_int(int fd, long request, int arg); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32}) + abstract long call_sysconf(int name); + + @TruffleBoundary + static String getLibPath(PythonContext context) { + String libPythonName = PythonContext.getSupportLibName(SUPPORTING_NATIVE_LIB_NAME); + TruffleFile homePath = context.getEnv().getInternalTruffleFile(context.getCAPIHome().toJavaStringUncached()); + TruffleFile file = homePath.resolve(libPythonName); + return file.getPath(); + } + + @TruffleBoundary + static NativeLibrary loadNativeLibrary(PythonContext context) { + String path = getLibPath(context); + try { + return context.ensureNativeContext().loadLibrary(path, PosixConstants.RTLD_LOCAL.value); + } catch (NativeLibraryLoadException e) { + throw new UnsupportedOperationException(String.format(""" + Could not load posix support library from path '%s'. Troubleshooting:\s + Check permissions of the file.""", path), e); + } + } + } + + abstract static class CryptNativeFunctionInvoker { + @DowncallSignature(returnType = POINTER, argumentTypes = {POINTER, POINTER}) + abstract long crypt(long word, long salt); + + @TruffleBoundary + static NativeLibrary loadNativeLibrary(PythonContext context) { + if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_DARWIN) { + return context.ensureNativeContext().getDefaultLibrary(); + } + /* + * We don't want to link the posix support library against libcrypt, because it might + * not be available on the target Linux system and would make the whole support library + * fail to load. Load it dynamically on demand instead. + */ + try { + return context.ensureNativeContext().loadLibrary("libcrypt.so", PosixConstants.RTLD_LOCAL.value); + } catch (NativeLibraryLoadException e) { + throw new UnsupportedOperationException("Could not load crypt support library 'libcrypt.so'.", e); + } + } + } + + private final PythonContext context; + private final TruffleString nativeBackend; + private final PosixNativeFunctionInvoker posixNativeFunctionInvoker; + private final CryptNativeFunctionInvoker cryptNativeFunctionInvoker; + @CompilationFinal(dimensions = 1) private long[] constantValues; + + public NativePosixSupport(PythonContext context, TruffleString nativeBackend) { + assert nativeBackend.equalsUncached(T_NATIVE, TS_ENCODING); + this.context = context; + this.nativeBackend = nativeBackend; + this.posixNativeFunctionInvoker = new PosixNativeFunctionInvokerGen(context); + this.cryptNativeFunctionInvoker = new CryptNativeFunctionInvokerGen(context); + setEnv(context.getEnv()); + } + + long getConstant(NativePosixConstants constant) { + if (constantValues == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + long[] values = new long[NativePosixConstants.values().length]; + long nativeValues = NativeMemory.mallocLongArray(values.length); + try { + int result = posixNativeFunctionInvoker.init_constants(nativeValues, values.length); + if (result != 0) { + throw CompilerDirectives.shouldNotReachHere("Mismatched build of posix native library"); + } + NativeMemory.readLongArrayElements(nativeValues, 0, values, 0, values.length); + constantValues = values; + } finally { + NativeMemory.free(nativeValues); + } + } + return constantValues[constant.ordinal()]; + } + + @Override + public void setEnv(Env env) { + if (env.isPreInitialization()) { + return; + } + // Java NIO (and TruffleFile) do not expect/support changing native working directory since + // it is inherently thread-unsafe operation. It is not defined how NIO behaves when native + // cwd changes, thus we need to prevent TruffleFile from resolving relative paths using + // NIO by setting Truffle cwd to a know value. This cannot be done lazily in chdir() because + // native cwd is global, but Truffle cwd is per context. + // TruffleFile will be unaware of the real working directory and keep resolving against the + // original working directory. This should not matter since we do not use TruffleFile for + // ordinary I/O when using the native backend. + try { + TruffleFile truffleFile = context.getEnv().getInternalTruffleFile(".").getAbsoluteFile(); + context.getEnv().setCurrentWorkingDirectory(truffleFile); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Unable to change Truffle working directory", e); + } + } + + @ExportMessage + public TruffleString getBackend() { + return nativeBackend; + } + + @ExportMessage + public TruffleString strerror(int errorCode, + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) { + // From man pages: The GNU C Library uses a buffer of 1024 characters for strerror(). + // This buffer size therefore should be sufficient to avoid an ERANGE error when calling + // strerror_r(). + long buf = NativeMemory.mallocByteArray(STRERROR_BUF_LENGTH); + try { + posixNativeFunctionInvoker.call_strerror(errorCode, buf, STRERROR_BUF_LENGTH); + // TODO PyUnicode_DecodeLocale + return zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, buf); + } finally { + NativeMemory.free(buf); + } + } + + @ExportMessage + public long getpid() { + return posixNativeFunctionInvoker.call_getpid(); + } + + @ExportMessage + public int umask(int mask) throws PosixException { + int result = posixNativeFunctionInvoker.call_umask(mask); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + return result; + } + + @ExportMessage + public int openat(int dirFd, Object pathname, int flags, int mode) throws PosixException { + long pathnamePtr = pathToNativeCString(pathname); + try { + int fd = posixNativeFunctionInvoker.call_openat(dirFd, pathnamePtr, flags, mode); + if (fd < 0) { + throw getErrnoAndThrowPosixException(); + } + return fd; + } finally { + NativeMemory.free(pathnamePtr); + } + } + + @ExportMessage + public int close(int fd) throws PosixException { + final int rv = posixNativeFunctionInvoker.call_close(fd); + if (rv < 0) { + throw getErrnoAndThrowPosixException(); + } + return rv; + } + + @ExportMessage + public Buffer read(int fd, long length) throws PosixException { + long count = Math.min(length, MAX_READ); + Buffer buffer = Buffer.allocate(count); + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(count); + try { + posixNativeFunctionInvoker.set_errno(0); + long n = posixNativeFunctionInvoker.call_read(fd, nativeBuffer, count); + if (n < 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuffer, 0, buffer.data, 0, (int) n); + return buffer.withLength(n); + } finally { + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public long write(int fd, Buffer data) throws PosixException { + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(data.length); + try { + NativeMemory.writeByteArrayElements(nativeBuffer, 0, data.data, 0, (int) data.length); + posixNativeFunctionInvoker.set_errno(0); + long n = posixNativeFunctionInvoker.call_write(fd, nativeBuffer, data.length); + if (n < 0) { + throw getErrnoAndThrowPosixException(); + } + return n; + } finally { + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public int dup(int fd) throws PosixException { + int newFd = posixNativeFunctionInvoker.call_dup(fd); + if (newFd < 0) { + throw getErrnoAndThrowPosixException(); + } + return newFd; + } + + @ExportMessage + public int dup2(int fd, int fd2, boolean inheritable) throws PosixException { + int newFd = posixNativeFunctionInvoker.call_dup2(fd, fd2, inheritable ? 1 : 0); + if (newFd < 0) { + throw getErrnoAndThrowPosixException(); + } + return newFd; + } + + @ExportMessage + public boolean getInheritable(int fd) throws PosixException { + int result = posixNativeFunctionInvoker.get_inheritable(fd); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + return result != 0; + } + + @ExportMessage + public void setInheritable(int fd, boolean inheritable) throws PosixException { + if (posixNativeFunctionInvoker.set_inheritable(fd, inheritable ? 1 : 0) < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public int[] pipe() throws PosixException { + int[] fds = new int[2]; + long nativeFds = NativeMemory.mallocIntArray(fds.length); + try { + if (posixNativeFunctionInvoker.call_pipe2(nativeFds) != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readIntArrayElements(nativeFds, 0, fds, 0, fds.length); + return fds; + } finally { + NativeMemory.free(nativeFds); + } + } + + @ExportMessage + public SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeval timeout) throws PosixException { + int largestFD = findMax(readfds, -1); + largestFD = findMax(writefds, largestFD); + largestFD = findMax(errorfds, largestFD); + // This will be treated as boolean array (output parameter), each item indicating if given + // FD was selected or not + byte[] selected = new byte[readfds.length + writefds.length + errorfds.length]; + int nfds = largestFD == -1 ? 0 : largestFD + 1; + long secs = -1, usecs = -1; + if (timeout != null) { + secs = timeout.getSeconds(); + usecs = timeout.getMicroseconds(); + } + long nativeReadFds = NULLPTR; + long nativeWriteFds = NULLPTR; + long nativeErrorFds = NULLPTR; + long nativeSelected = NULLPTR; + try { + nativeReadFds = NativeMemory.copyToNativeIntArrayOrNull(readfds); + nativeWriteFds = NativeMemory.copyToNativeIntArrayOrNull(writefds); + nativeErrorFds = NativeMemory.copyToNativeIntArrayOrNull(errorfds); + nativeSelected = NativeMemory.mallocByteArrayOrNull(selected.length); + int result = posixNativeFunctionInvoker.call_select(nfds, + nativeReadFds, readfds.length, + nativeWriteFds, writefds.length, + nativeErrorFds, errorfds.length, + secs, usecs, nativeSelected); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + if (selected.length > 0) { + NativeMemory.readByteArrayElements(nativeSelected, 0, selected, 0, selected.length); + } + } finally { + NativeMemory.free(nativeSelected); + NativeMemory.free(nativeErrorFds); + NativeMemory.free(nativeWriteFds); + NativeMemory.free(nativeReadFds); + } + return new SelectResult( + selectFillInResult(readfds, selected, 0), + selectFillInResult(writefds, selected, readfds.length), + selectFillInResult(errorfds, selected, readfds.length + writefds.length)); + + } + + private static boolean[] selectFillInResult(int[] fds, byte[] selected, int selectedOffset) { + boolean[] res = new boolean[fds.length]; + for (int i = 0; i < fds.length; i++) { + res[i] = selected[selectedOffset + i] != 0; + } + return res; + } + + private static int findMax(int[] items, int currentMax) { + int max = currentMax; + for (int item : items) { + if (item > max) { + max = item; + } + } + return max; + } + + @ExportMessage + public boolean poll(int fd, boolean forWriting, Timeval timeout) throws PosixException { + long secs = -1, usecs = -1; + if (timeout != null) { + secs = timeout.getSeconds(); + usecs = timeout.getMicroseconds(); + } + int result = posixNativeFunctionInvoker.call_poll(fd, forWriting ? 1 : 0, secs, usecs); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + if (result == 0) { + return false; + } else { + return true; + } + } + + @ExportMessage + public long lseek(int fd, long offset, int how) throws PosixException { + long res = posixNativeFunctionInvoker.call_lseek(fd, offset, how); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + @ExportMessage + public void ftruncate(int fd, long length) throws PosixException { + int res = posixNativeFunctionInvoker.call_ftruncate(fd, length); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public void truncate(Object path, long length) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + int res = posixNativeFunctionInvoker.call_truncate(pathPtr, length); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void fsync(int fd) throws PosixException { + int res = posixNativeFunctionInvoker.call_fsync(fd); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + void flock(int fd, int operation) throws PosixException { + int res = posixNativeFunctionInvoker.call_flock(fd, operation); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + void fcntlLock(int fd, boolean blocking, int lockType, int whence, long start, long length) throws PosixException { + int res = posixNativeFunctionInvoker.call_fcntl_lock(fd, blocking ? 1 : 0, lockType, whence, start, length); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public boolean getBlocking(int fd) throws PosixException { + int result = posixNativeFunctionInvoker.get_blocking(fd); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + return result != 0; + } + + @ExportMessage + public void setBlocking(int fd, boolean blocking) throws PosixException { + if (posixNativeFunctionInvoker.set_blocking(fd, blocking ? 1 : 0) < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public int[] getTerminalSize(int fd) throws PosixException { + int[] size = new int[2]; + long nativeSize = NativeMemory.mallocIntArray(size.length); + try { + if (posixNativeFunctionInvoker.get_terminal_size(fd, nativeSize) != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readIntArrayElements(nativeSize, 0, size, 0, size.length); + return size; + } finally { + NativeMemory.free(nativeSize); + } + } + + @ExportMessage + public long sysconf(int name) throws PosixException { + long result = posixNativeFunctionInvoker.call_sysconf(name); + if (result == -1) { + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno != 0) { + throw newPosixException(errno); + } + } + return result; + } + + @ExportMessage + public long[] fstatat(int dirFd, Object pathname, boolean followSymlinks) throws PosixException { + long[] out = new long[13]; + long nativeOut = NULLPTR; + long pathnamePtr = NULLPTR; + try { + nativeOut = NativeMemory.mallocLongArray(out.length); + pathnamePtr = pathToNativeCString(pathname); + int res = posixNativeFunctionInvoker.call_fstatat(dirFd, pathnamePtr, followSymlinks ? 1 : 0, nativeOut); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return out; + } finally { + NativeMemory.free(pathnamePtr); + NativeMemory.free(nativeOut); + } + } + + @ExportMessage + public long[] fstat(int fd) throws PosixException { + long[] out = new long[13]; + long nativeOut = NativeMemory.mallocLongArray(out.length); + try { + int res = posixNativeFunctionInvoker.call_fstat(fd, nativeOut); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return out; + } finally { + NativeMemory.free(nativeOut); + } + } + + @ExportMessage + public long[] statvfs(Object path) throws PosixException { + long[] out = new long[11]; + long nativeOut = NULLPTR; + long pathPtr = NULLPTR; + try { + nativeOut = NativeMemory.mallocLongArray(out.length); + pathPtr = pathToNativeCString(path); + int res = posixNativeFunctionInvoker.call_statvfs(pathPtr, nativeOut); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return out; + } finally { + NativeMemory.free(pathPtr); + NativeMemory.free(nativeOut); + } + } + + @ExportMessage + public long[] fstatvfs(int fd) throws PosixException { + long[] out = new long[11]; + long nativeOut = NativeMemory.mallocLongArray(out.length); + try { + int res = posixNativeFunctionInvoker.call_fstatvfs(fd, nativeOut); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return out; + } finally { + NativeMemory.free(nativeOut); + } + } + + @ExportMessage + public Object[] uname( + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws PosixException { + long sysPtr = NULLPTR; + long nodePtr = NULLPTR; + long relPtr = NULLPTR; + long verPtr = NULLPTR; + long machinePtr = NULLPTR; + try { + sysPtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + nodePtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + relPtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + verPtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + machinePtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + int res = posixNativeFunctionInvoker.call_uname(sysPtr, nodePtr, relPtr, verPtr, machinePtr, UNAME_BUF_LENGTH); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + return new Object[]{ + // TODO PyUnicode_DecodeFSDefault + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, sysPtr), + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, nodePtr), + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, relPtr), + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, verPtr), + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, machinePtr) + }; + } finally { + NativeMemory.free(machinePtr); + NativeMemory.free(verPtr); + NativeMemory.free(relPtr); + NativeMemory.free(nodePtr); + NativeMemory.free(sysPtr); + } + } + + @ExportMessage + public void unlinkat(int dirFd, Object pathname, boolean rmdir) throws PosixException { + long pathnamePtr = pathToNativeCString(pathname); + try { + int result = posixNativeFunctionInvoker.call_unlinkat(dirFd, pathnamePtr, rmdir ? 1 : 0); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathnamePtr); + } + } + + @ExportMessage + public void linkat(int oldFdDir, Object oldPath, int newFdDir, Object newPath, int flags) throws PosixException { + long oldPathPtr = NULLPTR; + long newPathPtr = NULLPTR; + try { + oldPathPtr = pathToNativeCString(oldPath); + newPathPtr = pathToNativeCString(newPath); + int result = posixNativeFunctionInvoker.call_linkat(oldFdDir, oldPathPtr, newFdDir, newPathPtr, flags); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(newPathPtr); + NativeMemory.free(oldPathPtr); + } + } + + @ExportMessage + public void symlinkat(Object target, int linkpathDirFd, Object linkpath) throws PosixException { + long targetPtr = NULLPTR; + long linkpathPtr = NULLPTR; + try { + targetPtr = pathToNativeCString(target); + linkpathPtr = pathToNativeCString(linkpath); + int result = posixNativeFunctionInvoker.call_symlinkat(targetPtr, linkpathDirFd, linkpathPtr); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(linkpathPtr); + NativeMemory.free(targetPtr); + } + } + + @ExportMessage + public void mkdirat(int dirFd, Object pathname, int mode) throws PosixException { + long pathnamePtr = pathToNativeCString(pathname); + try { + int result = posixNativeFunctionInvoker.call_mkdirat(dirFd, pathnamePtr, mode); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathnamePtr); + } + } + + @ExportMessage + public Object getcwd() throws PosixException { + for (int bufLen = 1024;; bufLen += 1024) { + Buffer buffer = Buffer.allocate(bufLen); + long nativeBuffer = NativeMemory.mallocByteArray(bufLen); + try { + int n = posixNativeFunctionInvoker.call_getcwd(nativeBuffer, bufLen); + if (n == 0) { + NativeMemory.readByteArrayElements(nativeBuffer, 0, buffer.data, 0, bufLen); + buffer = buffer.withLength(findZero(buffer.data)); + return buffer; + } + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno != OSErrorEnum.ERANGE.getNumber()) { + throw newPosixException(errno); + } + } finally { + NativeMemory.free(nativeBuffer); + } + } + } + + @ExportMessage + public void chdir(Object path) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + int result = posixNativeFunctionInvoker.call_chdir(pathPtr); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void fchdir(int fd) throws PosixException { + int result = posixNativeFunctionInvoker.call_fchdir(fd); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public boolean isatty(int fd) { + return posixNativeFunctionInvoker.call_isatty(fd) != 0; + } + + @ExportMessage + public Object opendir(Object path) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + long ptr = posixNativeFunctionInvoker.call_opendir(pathPtr); + if (ptr == 0) { + throw getErrnoAndThrowPosixException(); + } + return ptr; + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public Object fdopendir(int fd) throws PosixException { + long ptr = posixNativeFunctionInvoker.call_fdopendir(fd); + if (ptr == 0) { + throw getErrnoAndThrowPosixException(); + } + return ptr; + } + + @ExportMessage + public void closedir(Object dirStreamObj) throws PosixException { + int res = posixNativeFunctionInvoker.call_closedir(((Long) dirStreamObj).longValue()); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public Object readdir(Object dirStreamObj) throws PosixException { + Buffer name = Buffer.allocate(DIRENT_NAME_BUF_LENGTH); + long[] out = new long[2]; + long dirStream = ((Long) dirStreamObj).longValue(); + long nativeName = NULLPTR; + long nativeOut = NULLPTR; + try { + nativeName = NativeMemory.mallocByteArray(DIRENT_NAME_BUF_LENGTH); + nativeOut = NativeMemory.mallocLongArray(out.length); + int result; + do { + result = posixNativeFunctionInvoker.call_readdir(dirStream, nativeName, DIRENT_NAME_BUF_LENGTH, nativeOut); + if (result != 0) { + NativeMemory.readByteArrayElements(nativeName, 0, name.data, 0, name.data.length); + } + } while (result != 0 && name.data[0] == '.' && (name.data[1] == 0 || (name.data[1] == '.' && name.data[2] == 0))); + if (result != 0) { + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return new DirEntry(name.withLength(findZero(name.data)), out[0], (int) out[1]); + } + } finally { + NativeMemory.free(nativeOut); + NativeMemory.free(nativeName); + } + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno == 0) { + return null; + } + throw newPosixException(errno); + } + + @ExportMessage + public void rewinddir(Object dirStreamObj) { + posixNativeFunctionInvoker.call_rewinddir(((Long) dirStreamObj).longValue()); + } + + @ExportMessage + @SuppressWarnings("static-method") + public Object dirEntryGetName(Object dirEntryObj) { + DirEntry dirEntry = (DirEntry) dirEntryObj; + return dirEntry.name; + } + + @ExportMessage + public static class DirEntryGetPath { + @Specialization(guards = "endsWithSlash(scandirPath)") + static Buffer withSlash(@SuppressWarnings("unused") NativePosixSupport receiver, DirEntry dirEntry, Object scandirPath) { + Buffer scandirPathBuffer = (Buffer) scandirPath; + int pathLen = scandirPathBuffer.data.length; + int nameLen = (int) dirEntry.name.length; + byte[] buf = new byte[pathLen + nameLen]; + PythonUtils.arraycopy(scandirPathBuffer.data, 0, buf, 0, pathLen); + PythonUtils.arraycopy(dirEntry.name.data, 0, buf, pathLen, nameLen); + return Buffer.wrap(buf); + } + + @Specialization(guards = "!endsWithSlash(scandirPath)") + static Buffer withoutSlash(@SuppressWarnings("unused") NativePosixSupport receiver, DirEntry dirEntry, Object scandirPath) { + Buffer scandirPathBuffer = (Buffer) scandirPath; + int pathLen = scandirPathBuffer.data.length; + int nameLen = (int) dirEntry.name.length; + byte[] buf = new byte[pathLen + 1 + nameLen]; + PythonUtils.arraycopy(scandirPathBuffer.data, 0, buf, 0, pathLen); + buf[pathLen] = POSIX_FILENAME_SEPARATOR; + PythonUtils.arraycopy(dirEntry.name.data, 0, buf, pathLen + 1, nameLen); + return Buffer.wrap(buf); + } + + protected static boolean endsWithSlash(Object path) { + Buffer b = (Buffer) path; + return b.data[b.data.length - 1] == POSIX_FILENAME_SEPARATOR; + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public long dirEntryGetInode(Object dirEntry) { + DirEntry entry = (DirEntry) dirEntry; + return entry.ino; + } + + @ExportMessage + @SuppressWarnings("static-method") + public int dirEntryGetType(Object dirEntryObj) { + DirEntry dirEntry = (DirEntry) dirEntryObj; + return dirEntry.type; + } + + @ExportMessage + public void utimensat(int dirFd, Object pathname, long[] timespec, boolean followSymlinks) throws PosixException { + assert PosixConstants.HAVE_UTIMENSAT.value; + assert timespec == null || timespec.length == 4; + long pathnamePtr = NULLPTR; + long timespecPtr = NULLPTR; + try { + pathnamePtr = pathToNativeCString(pathname); + timespecPtr = timespec == null ? NULLPTR : NativeMemory.copyToNativeLongArray(timespec); + int ret = posixNativeFunctionInvoker.call_utimensat(dirFd, pathnamePtr, timespecPtr, followSymlinks ? 1 : 0); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timespecPtr); + NativeMemory.free(pathnamePtr); + } + } + + @ExportMessage + public void futimens(int fd, long[] timespec) throws PosixException { + assert PosixConstants.HAVE_FUTIMENS.value; + assert timespec == null || timespec.length == 4; + long timespecPtr = timespec == null ? NULLPTR : NativeMemory.copyToNativeLongArray(timespec); + try { + int ret = posixNativeFunctionInvoker.call_futimens(fd, timespecPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timespecPtr); + } + } + + @ExportMessage + public void futimes(int fd, Timeval[] timeval) throws PosixException { + assert timeval == null || timeval.length == 2; + long timevalPtr = copyTimevalArrayToNativeOrNull(timeval); + try { + int ret = posixNativeFunctionInvoker.call_futimes(fd, timevalPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timevalPtr); + } + } + + @ExportMessage + public void lutimes(Object filename, Timeval[] timeval) throws PosixException { + assert timeval == null || timeval.length == 2; + long filenamePtr = NULLPTR; + long timevalPtr = NULLPTR; + try { + filenamePtr = pathToNativeCString(filename); + timevalPtr = copyTimevalArrayToNativeOrNull(timeval); + int ret = posixNativeFunctionInvoker.call_lutimes(filenamePtr, timevalPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timevalPtr); + NativeMemory.free(filenamePtr); + } + } + + @ExportMessage + public void utimes(Object filename, Timeval[] timeval) throws PosixException { + assert timeval == null || timeval.length == 2; + long filenamePtr = NULLPTR; + long timevalPtr = NULLPTR; + try { + filenamePtr = pathToNativeCString(filename); + timevalPtr = copyTimevalArrayToNativeOrNull(timeval); + int ret = posixNativeFunctionInvoker.call_utimes(filenamePtr, timevalPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timevalPtr); + NativeMemory.free(filenamePtr); + } + } + + @ExportMessage + public void renameat(int oldDirFd, Object oldPath, int newDirFd, Object newPath) throws PosixException { + long oldPathPtr = NULLPTR; + long newPathPtr = NULLPTR; + try { + oldPathPtr = pathToNativeCString(oldPath); + newPathPtr = pathToNativeCString(newPath); + int ret = posixNativeFunctionInvoker.call_renameat(oldDirFd, oldPathPtr, newDirFd, newPathPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(newPathPtr); + NativeMemory.free(oldPathPtr); + } + } + + @ExportMessage + public boolean faccessat(int dirFd, Object path, int mode, boolean effectiveIds, boolean followSymlinks) { + long pathPtr = pathToNativeCString(path); + int ret; + try { + ret = posixNativeFunctionInvoker.call_faccessat(dirFd, pathPtr, mode, effectiveIds ? 1 : 0, followSymlinks ? 1 : 0); + } finally { + NativeMemory.free(pathPtr); + } + if (ret != 0 && LOGGER.isLoggable(Level.FINE)) { + log(Level.FINE, "faccessat return value: %d, errno: %d", ret, posixNativeFunctionInvoker.get_errno()); + } + return ret == 0; + } + + @ExportMessage + public void fchmodat(int dirFd, Object path, int mode, boolean followSymlinks) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + int ret = posixNativeFunctionInvoker.call_fchmodat(dirFd, pathPtr, mode, followSymlinks ? 1 : 0); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void fchmod(int fd, int mode) throws PosixException { + int ret = posixNativeFunctionInvoker.call_fchmod(fd, mode); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + int ret = posixNativeFunctionInvoker.call_fchownat(dirFd, pathPtr, owner, group, followSymlinks ? 1 : 0); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void fchown(int fd, long owner, long group) throws PosixException { + int ret = posixNativeFunctionInvoker.call_fchown(fd, owner, group); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public Object readlinkat(int dirFd, Object path) throws PosixException { + Buffer buffer = Buffer.allocate(PATH_MAX.value); + long pathPtr = pathToNativeCString(path); + try { + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(PATH_MAX.value); + try { + long n = posixNativeFunctionInvoker.call_readlinkat(dirFd, pathPtr, nativeBuffer, PATH_MAX.value); + if (n < 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuffer, 0, buffer.data, 0, (int) n); + return buffer.withLength(n); + } finally { + NativeMemory.free(nativeBuffer); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void kill(long pid, int signal) throws PosixException { + int res = posixNativeFunctionInvoker.call_kill(pid, signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public void raise(int signal) throws PosixException { + int res = posixNativeFunctionInvoker.call_raise(signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public int alarm(int seconds) { + return posixNativeFunctionInvoker.call_alarm(seconds); + } + + @ExportMessage + public Timeval[] getitimer(int which) throws PosixException { + long nativeCurrentValue = NativeMemory.mallocLongArray(4); + try { + int res = posixNativeFunctionInvoker.call_getitimer(which, nativeCurrentValue); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + return unwrapTimeval(nativeCurrentValue); + } finally { + NativeMemory.free(nativeCurrentValue); + } + } + + @ExportMessage + public Timeval[] setitimer(int which, Timeval delay, Timeval interval) throws PosixException { + long nativeNewValue = NULLPTR; + long nativeOldValue = NULLPTR; + try { + nativeNewValue = wrapItimerval(delay, interval); + nativeOldValue = NativeMemory.mallocLongArray(4); + int res = posixNativeFunctionInvoker.call_setitimer(which, nativeNewValue, nativeOldValue); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + return unwrapTimeval(nativeOldValue); + } finally { + NativeMemory.free(nativeOldValue); + NativeMemory.free(nativeNewValue); + } + } + + @ExportMessage + public void signalSelf(int signal) throws PosixException { + if (!ImageInfo.inImageRuntimeCode()) { + throw new UnsupportedPosixFeatureException("self-signals are only supported in native standalone"); + } + int res = posixNativeFunctionInvoker.signal_self(signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public void killpg(long pgid, int signal) throws PosixException { + int res = posixNativeFunctionInvoker.call_killpg(pgid, signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public long[] waitpid(long pid, int options, + @Bind Node node) throws PosixException { + boolean hasNohang = (options & WNOHANG.getValueIfDefined()) != 0; + int subOptions = options | WNOHANG.getValueIfDefined(); + long nativeStatus = NativeMemory.callocIntArray(1); + try { + long res = posixNativeFunctionInvoker.call_waitpid(pid, nativeStatus, subOptions); + while (res == 0 && !hasNohang) { + TruffleSafepoint.setBlockedThreadInterruptible(node, (ignored) -> { + Thread.sleep(20); + }, null); + res = posixNativeFunctionInvoker.call_waitpid(pid, nativeStatus, subOptions); + } + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return new long[]{res, NativeMemory.readInt(nativeStatus)}; + } finally { + NativeMemory.free(nativeStatus); + } + } + + @ExportMessage + public boolean wcoredump(int status) { + return posixNativeFunctionInvoker.call_wcoredump(status) != 0; + } + + @ExportMessage + public boolean wifcontinued(int status) { + return posixNativeFunctionInvoker.call_wifcontinued(status) != 0; + } + + @ExportMessage + public boolean wifstopped(int status) { + return posixNativeFunctionInvoker.call_wifstopped(status) != 0; + } + + @ExportMessage + public boolean wifsignaled(int status) { + return posixNativeFunctionInvoker.call_wifsignaled(status) != 0; + } + + @ExportMessage + public boolean wifexited(int status) { + return posixNativeFunctionInvoker.call_wifexited(status) != 0; + } + + @ExportMessage + public int wexitstatus(int status) { + return posixNativeFunctionInvoker.call_wexitstatus(status); + } + + @ExportMessage + public int wtermsig(int status) { + return posixNativeFunctionInvoker.call_wtermsig(status); + } + + @ExportMessage + public int wstopsig(int status) { + return posixNativeFunctionInvoker.call_wstopsig(status); + } + + @ExportMessage + public long getuid() { + return posixNativeFunctionInvoker.call_getuid(); + } + + @ExportMessage + public long geteuid() { + return posixNativeFunctionInvoker.call_geteuid(); + } + + @ExportMessage + public long getgid() { + return posixNativeFunctionInvoker.call_getgid(); + } + + @ExportMessage + public long getegid() { + return posixNativeFunctionInvoker.call_getegid(); + } + + @ExportMessage + public long getppid() { + return posixNativeFunctionInvoker.call_getppid(); + } + + @ExportMessage + public void setpgid(long pid, long pgid) throws PosixException { + int res = posixNativeFunctionInvoker.call_setpgid(pid, pgid); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public long getpgid(long pid) throws PosixException { + long res = posixNativeFunctionInvoker.call_getpgid(pid); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + @ExportMessage + public long getpgrp() { + return posixNativeFunctionInvoker.call_getpgrp(); + } + + @ExportMessage + public long getsid(long pid) throws PosixException { + long res = posixNativeFunctionInvoker.call_getsid(pid); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + @ExportMessage + public long setsid() throws PosixException { + long res = posixNativeFunctionInvoker.call_setsid(); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + @ExportMessage + public long[] getgroups() throws PosixException { + // The first call gets us the number of groups, so we can allocate the output array + int res = posixNativeFunctionInvoker.call_getgroups(0, NULLPTR); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + if (res == 0) { + return EMPTY_LONG_ARRAY; + } + long[] groups = new long[res]; + long nativeGroups = NativeMemory.mallocLongArray(groups.length); + try { + res = posixNativeFunctionInvoker.call_getgroups(groups.length, nativeGroups); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeGroups, 0, groups, 0, groups.length); + return groups; + } finally { + NativeMemory.free(nativeGroups); + } + } + + @ExportMessage + public RusageResult getrusage(int who) throws PosixException { + long nativeResult = NativeMemory.mallocLongArray(16); + try { + int res = posixNativeFunctionInvoker.call_getrusage(who, nativeResult); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return new RusageResult( + Double.longBitsToDouble(NativeMemory.readLongArrayElement(nativeResult, 0)), + Double.longBitsToDouble(NativeMemory.readLongArrayElement(nativeResult, 1)), + NativeMemory.readLongArrayElement(nativeResult, 2), + NativeMemory.readLongArrayElement(nativeResult, 3), + NativeMemory.readLongArrayElement(nativeResult, 4), + NativeMemory.readLongArrayElement(nativeResult, 5), + NativeMemory.readLongArrayElement(nativeResult, 6), + NativeMemory.readLongArrayElement(nativeResult, 7), + NativeMemory.readLongArrayElement(nativeResult, 8), + NativeMemory.readLongArrayElement(nativeResult, 9), + NativeMemory.readLongArrayElement(nativeResult, 10), + NativeMemory.readLongArrayElement(nativeResult, 11), + NativeMemory.readLongArrayElement(nativeResult, 12), + NativeMemory.readLongArrayElement(nativeResult, 13), + NativeMemory.readLongArrayElement(nativeResult, 14), + NativeMemory.readLongArrayElement(nativeResult, 15)); + } finally { + NativeMemory.free(nativeResult); + } + } + + @ExportMessage + public OpenPtyResult openpty() throws PosixException { + long nativeOutvars = NativeMemory.mallocIntArray(2); + try { + int res = posixNativeFunctionInvoker.call_openpty(nativeOutvars); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + return new OpenPtyResult( + NativeMemory.readIntArrayElement(nativeOutvars, 0), + NativeMemory.readIntArrayElement(nativeOutvars, 1)); + } finally { + NativeMemory.free(nativeOutvars); + } + } + + @ExportMessage + public TruffleString ctermid( + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws PosixException { + long nativeBuf = NativeMemory.mallocByteArray(L_ctermid.value); + try { + int res = posixNativeFunctionInvoker.call_ctermid(nativeBuf); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + // TODO PyUnicode_DecodeFSDefault + return zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, nativeBuf); + } finally { + NativeMemory.free(nativeBuf); + } + } + + @ExportMessage + public void setenv(Object name, Object value, boolean overwrite) throws PosixException { + long namePtr = NULLPTR; + long valuePtr = NULLPTR; + try { + namePtr = pathToNativeCString(name); + valuePtr = pathToNativeCString(value); + int res = posixNativeFunctionInvoker.call_setenv(namePtr, valuePtr, overwrite ? 1 : 0); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(valuePtr); + NativeMemory.free(namePtr); + } + } + + @ExportMessage + public void unsetenv(Object name) throws PosixException { + long namePtr = pathToNativeCString(name); + try { + int res = posixNativeFunctionInvoker.call_unsetenv(namePtr); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(namePtr); + } + } + + @ExportMessage + public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd, + int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, boolean allowVFork) throws PosixException { + + // The following strings and string arrays need to be present in the native function: + // - char** of executable names ('\0'-terminated strings with an extra NULL at the end) + // - char** of arguments ('\0'-terminated strings with an extra NULL at the end) + // - an optional char** of env variables ('\0'-terminated strings with an extra NULL at the + // end), must distinguish between NULL (child inherits env) and an empty array (child gets + // empty env) + // - an optional char* cwd ('\0'-terminated string or NULL) + // We do this by concatenating all strings (including their terminating '\0' characters) + // into one large byte buffer (which becomes 'char *') and pass an additional array of + // offsets to mark where the individual strings begin. To prevent memory allocation + // in C (and related free()), we reuse this array of integer offsets as an array of + // C-strings (char **). For this reason, the array of offsets is allocated as long[]. + // In the offsets array we mark the places where NULL should be with a special value -1. + // All that is left is to let the native function know where in the offsets array the + // individual string arrays begin: + // - executable names are always at index 0 + // - argsPos is the index in the offsets array pointing to the first argument + // - envPos is either -1 or an index in the offsets array pointing to the first env string + // - cwdPos is either -1 or an index in the offsets array pointing to the cwd string + + // First we calculate the lengths of the offsets array and the string buffer (dataLen). + int offsetsLen; + int argsPos; + int envPos; + int cwdPos; + long dataLen; + + try { + offsetsLen = executables.length + 1; + dataLen = addLengthsOfCStrings(0, executables); + + argsPos = offsetsLen; + offsetsLen += args.length + 1; + dataLen = addLengthsOfCStrings(dataLen, args); + + if (env != null) { + envPos = offsetsLen; + offsetsLen += env.length + 1; + dataLen = addLengthsOfCStrings(dataLen, env); + } else { + envPos = -1; + } + + if (cwd != null) { + cwdPos = offsetsLen; + offsetsLen += 1; + // The +1 in the second argument can overflow only if the buffer contains 2^63-1 + // bytes, which is impossible since we are using Java arrays limited to 2^31-1. + dataLen = PythonUtils.addExact(dataLen, ((Buffer) cwd).length + 1L); + } else { + cwdPos = -1; + } + } catch (OverflowException e) { + throw newPosixException(OSErrorEnum.E2BIG.getNumber()); + } + + // This also guarantees that offsetsLen did not overflow: we add +1 to dataLen for each + // '\0', i.e. dataLen >= "number of strings" and offsetsLen < "number of strings" + 3 + // (3 accounts for the NULL terminating the executables, args and env arrays). + if (dataLen >= Integer.MAX_VALUE - 3) { + throw newPosixException(OSErrorEnum.E2BIG.getNumber()); + } + + byte[] data = new byte[(int) dataLen]; + long[] offsets = new long[offsetsLen]; + long offset = 0; + + offset = encodeCStringArray(data, offset, offsets, 0, executables); + offset = encodeCStringArray(data, offset, offsets, argsPos, args); + if (env != null) { + offset = encodeCStringArray(data, offset, offsets, envPos, env); + } + if (cwd != null) { + Buffer buf = (Buffer) cwd; + int strLen = (int) buf.length; + PythonUtils.arraycopy(buf.data, 0, data, (int) offset, strLen); + offsets[cwdPos] = offset; + offset += strLen + 1L; + } + assert offset == dataLen; + + long nativeData = NULLPTR; + long nativeOffsets = NULLPTR; + long nativeFdsToKeep = NULLPTR; + try { + nativeData = NativeMemory.copyToNativeByteArray(data); + nativeOffsets = NativeMemory.copyToNativeLongArray(offsets); + nativeFdsToKeep = NativeMemory.copyToNativeIntArrayOrNull(fdsToKeep); + int res = posixNativeFunctionInvoker.fork_exec(nativeData, nativeOffsets, offsets.length, argsPos, envPos, cwdPos, + stdinReadFd, stdinWriteFd, + stdoutReadFd, stdoutWriteFd, + stderrReadFd, stderrWriteFd, + errPipeReadFd, errPipeWriteFd, + closeFds ? 1 : 0, + restoreSignals ? 1 : 0, + callSetsid ? 1 : 0, + pgidToSet, + allowVFork ? 1 : 0, + nativeFdsToKeep, fdsToKeep.length); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + return res; + } finally { + NativeMemory.free(nativeFdsToKeep); + NativeMemory.free(nativeOffsets); + NativeMemory.free(nativeData); + } + } + + @ExportMessage + public void execv(Object pathname, Object[] args) throws PosixException { + + // The following strings and string arrays need to be present in the native function: + // - char* - the pathname ('\0'-terminated string) + // - char** of arguments ('\0'-terminated strings with an extra NULL at the end) + // We do this by concatenating all strings (including their terminating '\0' characters) + // into one large byte buffer (which becomes 'char *') and pass an additional array of + // offsets to mark where the individual strings begin. To prevent memory allocation + // in C (and related free()), we reuse this array of integer offsets as an array of + // C-strings (char **). For this reason, the array of offsets is allocated as long[]. + // In the offsets array we mark the places where NULL should be with a special value -1. + // - the pathname is always at index 0 + // - the arguments start at index 1 + + // First we calculate the lengths of the offsets array and the string buffer (dataLen). + int offsetsLen = 1 + args.length + 1; + long pathnameLen = ((Buffer) pathname).length; + long dataLen; + + try { + // The +1 can overflow only if the buffer contains 2^63-1 bytes, which is impossible + // since we are using Java arrays limited to 2^31-1. + dataLen = addLengthsOfCStrings(pathnameLen + 1L, args); + } catch (OverflowException e) { + throw newPosixException(OSErrorEnum.E2BIG.getNumber()); + } + + // This also guarantees that offsetsLen did not overflow: we add +1 to dataLen for each + // '\0', i.e. dataLen >= "number of strings" and offsetsLen == "number of strings" + 1 + // (1 accounts for the NULL terminating the args array). + // Also, dataLen > pathnameLen, so this check makes sure that the cast of pathnameLen to int + // below is safe. + if (dataLen >= Integer.MAX_VALUE - 1) { + throw newPosixException(OSErrorEnum.E2BIG.getNumber()); + } + + byte[] data = new byte[(int) dataLen]; + long[] offsets = new long[offsetsLen]; + + PythonUtils.arraycopy(((Buffer) pathname).data, 0, data, 0, (int) pathnameLen); + long offset = encodeCStringArray(data, pathnameLen + 1L, offsets, 1, args); + assert offset == dataLen; + + long nativeData = NULLPTR; + long nativeOffsets = NULLPTR; + try { + nativeData = NativeMemory.copyToNativeByteArray(data); + nativeOffsets = NativeMemory.copyToNativeLongArray(offsets); + posixNativeFunctionInvoker.call_execv(nativeData, nativeOffsets, offsets.length); + throw getErrnoAndThrowPosixException(); + } finally { + NativeMemory.free(nativeOffsets); + NativeMemory.free(nativeData); + } + } + + @ExportMessage + public int system(Object command) { + long commandPtr = pathToNativeCString(command); + try { + return posixNativeFunctionInvoker.call_system(commandPtr); + } finally { + NativeMemory.free(commandPtr); + } + } + + private static long addLengthsOfCStrings(long prevLen, Object[] src) throws OverflowException { + long len = prevLen; + for (Object o : src) { + len = PythonUtils.addExact(len, ((Buffer) o).length); + } + return PythonUtils.addExact(len, src.length); // add space for terminating '\0' + } + + /** + * Copies null-terminated strings to a buffer {@code data} starting at position {@code offset}, + * and stores the offset of each string to the {@code offsets} array starting at index + * {@code startPos}. + */ + private static long encodeCStringArray(byte[] data, long startOffset, long[] offsets, int startPos, Object[] src) { + // The code that calculates dataLen already checked that there is no overflow and that all + // offsets fit into an int. + long offset = startOffset; + for (int i = 0; i < src.length; ++i) { + Buffer buf = (Buffer) src[i]; + int strLen = (int) buf.length; + PythonUtils.arraycopy(buf.data, 0, data, (int) offset, strLen); + offsets[startPos + i] = offset; + offset += strLen + 1; // +1 for the terminating \0 character + } + offsets[startPos + src.length] = -1; // this will become NULL in C (the char* array + // needs to be terminated by a NULL) + return offset; + } + + private static final class MMapHandle { + private final long pointer; + private final long length; + + public MMapHandle(long pointer, long length) { + this.pointer = pointer; + this.length = length; + } + } + + @ExportMessage + public Object mmap(long length, int prot, int flags, int fd, long offset) throws PosixException { + long address = posixNativeFunctionInvoker.call_mmap(length, prot, flags, fd, offset); + if (address == 0) { + throw getErrnoAndThrowPosixException(); + } + return new MMapHandle(address, length); + } + + @ExportMessage + @SuppressWarnings("static-method") + public byte mmapReadByte(Object mmap, long index) { + MMapHandle handle = (MMapHandle) mmap; + if (index < 0 || index >= handle.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IndexOutOfBoundsException(); + } + return UNSAFE.getByte(handle.pointer + index); + } + + @ExportMessage + @SuppressWarnings("static-method") + public void mmapWriteByte(Object mmap, long index, byte value) { + MMapHandle handle = (MMapHandle) mmap; + checkIndexAndLen(handle, index, 1); + UNSAFE.putByte(handle.pointer + index, value); + } + + @ExportMessage + @SuppressWarnings("static-method") + public int mmapReadBytes(Object mmap, long index, byte[] bytes, int length) { + MMapHandle handle = (MMapHandle) mmap; + checkIndexAndLen(handle, index, length); + UNSAFE.copyMemory(null, handle.pointer + index, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length); + return length; + } + + @ExportMessage + @SuppressWarnings("static-method") + public void mmapWriteBytes(Object mmap, long index, byte[] bytes, int length) { + MMapHandle handle = (MMapHandle) mmap; + checkIndexAndLen(handle, index, length); + UNSAFE.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, handle.pointer + index, length); + } + + @ExportMessage + public void mmapFlush(Object mmap, long offset, long length) { + MMapHandle handle = (MMapHandle) mmap; + checkIndexAndLen(handle, offset, length); + posixNativeFunctionInvoker.call_msync(handle.pointer, offset, length); + } + + @ExportMessage + public void mmapUnmap(Object mmap, long length) throws PosixException { + MMapHandle handle = (MMapHandle) mmap; + if (length != handle.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException(); + } + int result = posixNativeFunctionInvoker.call_munmap(handle.pointer, length); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public long mmapGetPointer(Object mmap) { + MMapHandle handle = (MMapHandle) mmap; + return handle.pointer; + } + + private static void checkIndexAndLen(MMapHandle handle, long index, long length) { + if (length < 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException(); + } + if (index < 0 || index + length > handle.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IndexOutOfBoundsException(); + } + } + + @ExportMessage + public int socket(int domain, int type, int protocol) throws PosixException { + int result = posixNativeFunctionInvoker.call_socket(domain, type, protocol); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + return result; + } + + @ExportMessage + public AcceptResult accept(int sockfd) throws PosixException { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + long nativeAddr = NULLPTR; + long nativeAddrLen = NULLPTR; + try { + nativeAddr = NativeMemory.mallocByteArray(addr.data.length); + nativeAddrLen = NativeMemory.mallocIntArray(1); + int result = posixNativeFunctionInvoker.call_accept(sockfd, nativeAddr, nativeAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + readNativeSockAddr(nativeAddr, nativeAddrLen, addr); + return new AcceptResult(result, addr); + } finally { + NativeMemory.free(nativeAddrLen); + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public void bind(int sockfd, UniversalSockAddr usa) throws PosixException { + UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; + int addrLen = addr.getLen(); + long nativeAddr = NULLPTR; + try { + nativeAddr = NativeMemory.copyToNativeByteArray(addr.data, 0, addrLen); + int result = posixNativeFunctionInvoker.call_bind(sockfd, nativeAddr, addrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public void connect(int sockfd, UniversalSockAddr usa) throws PosixException { + UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; + int addrLen = addr.getLen(); + long nativeAddr = NULLPTR; + try { + nativeAddr = NativeMemory.copyToNativeByteArray(addr.data, 0, addrLen); + int result = posixNativeFunctionInvoker.call_connect(sockfd, nativeAddr, addrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public void listen(int sockfd, int backlog) throws PosixException { + int result = posixNativeFunctionInvoker.call_listen(sockfd, backlog); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public UniversalSockAddr getpeername(int sockfd) throws PosixException { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + long nativeAddr = NULLPTR; + long nativeAddrLen = NULLPTR; + try { + nativeAddr = NativeMemory.mallocByteArray(addr.data.length); + nativeAddrLen = NativeMemory.mallocIntArray(1); + int result = posixNativeFunctionInvoker.call_getpeername(sockfd, nativeAddr, nativeAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + readNativeSockAddr(nativeAddr, nativeAddrLen, addr); + return addr; + } finally { + NativeMemory.free(nativeAddrLen); + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public UniversalSockAddr getsockname(int sockfd) throws PosixException { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + long nativeAddr = NULLPTR; + long nativeAddrLen = NULLPTR; + try { + nativeAddr = NativeMemory.mallocByteArray(addr.data.length); + nativeAddrLen = NativeMemory.mallocIntArray(1); + int result = posixNativeFunctionInvoker.call_getsockname(sockfd, nativeAddr, nativeAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + readNativeSockAddr(nativeAddr, nativeAddrLen, addr); + return addr; + } finally { + NativeMemory.free(nativeAddrLen); + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public int send(int sockfd, byte[] buf, int offset, int len, int flags) throws PosixException { + checkBounds(buf, offset, len); + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); + try { + NativeMemory.writeByteArrayElements(nativeBuffer, 0, buf, offset, len); + posixNativeFunctionInvoker.set_errno(0); + int result = posixNativeFunctionInvoker.call_send(sockfd, nativeBuffer, len, flags); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + return result; + } finally { + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public int sendto(int sockfd, byte[] buf, int offset, int len, int flags, UniversalSockAddr usa) throws PosixException { + checkBounds(buf, offset, len); + UniversalSockAddrImpl destAddr = (UniversalSockAddrImpl) usa; + int destAddrLen = destAddr.getLen(); + long nativeBuffer = NULLPTR; + long nativeDestAddr = NULLPTR; + try { + nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); + NativeMemory.writeByteArrayElements(nativeBuffer, 0, buf, offset, len); + nativeDestAddr = NativeMemory.mallocByteArrayOrNull(destAddrLen); + NativeMemory.writeByteArrayElements(nativeDestAddr, 0, destAddr.data, 0, destAddrLen); + posixNativeFunctionInvoker.set_errno(0); + int result = posixNativeFunctionInvoker.call_sendto(sockfd, nativeBuffer, 0, len, flags, nativeDestAddr, destAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + return result; + } finally { + NativeMemory.free(nativeDestAddr); + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public int recv(int sockfd, byte[] buf, int offset, int len, int flags) throws PosixException { + checkBounds(buf, offset, len); + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); + try { + posixNativeFunctionInvoker.set_errno(0); + int result = posixNativeFunctionInvoker.call_recv(sockfd, nativeBuffer, len, flags); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuffer, 0, buf, offset, result); + return result; + } finally { + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public RecvfromResult recvfrom(int sockfd, byte[] buf, int offset, int len, int flags) throws PosixException { + checkBounds(buf, offset, len); + UniversalSockAddrImpl srcAddr = new UniversalSockAddrImpl(this); + long nativeBuffer = NULLPTR; + long nativeSrcAddr = NULLPTR; + long nativeAddrLen = NULLPTR; + try { + nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); + nativeSrcAddr = NativeMemory.mallocByteArray(srcAddr.data.length); + nativeAddrLen = NativeMemory.mallocIntArray(1); + posixNativeFunctionInvoker.set_errno(0); + int result = posixNativeFunctionInvoker.call_recvfrom(sockfd, nativeBuffer, 0, len, flags, nativeSrcAddr, nativeAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuffer, 0, buf, offset, result); + readNativeSockAddr(nativeSrcAddr, nativeAddrLen, srcAddr); + return new RecvfromResult(result, srcAddr); + } finally { + NativeMemory.free(nativeAddrLen); + NativeMemory.free(nativeSrcAddr); + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public void shutdown(int sockfd, int how) throws PosixException { + int res = posixNativeFunctionInvoker.call_shutdown(sockfd, how); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public int getsockopt(int sockfd, int level, int optname, byte[] optval, int optlen) throws PosixException { + assert optlen >= 0 && optval.length >= optlen; + long nativeOptval = NULLPTR; + long nativeBufLen = NULLPTR; + try { + nativeOptval = NativeMemory.mallocByteArray(Math.max(optlen, 1)); + nativeBufLen = NativeMemory.mallocIntArray(1); + NativeMemory.writeInt(nativeBufLen, optlen); + int res = posixNativeFunctionInvoker.call_getsockopt(sockfd, level, optname, nativeOptval, nativeBufLen); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + int actualLen = NativeMemory.readInt(nativeBufLen); + if (actualLen < 0 || actualLen > optval.length) { + throw CompilerDirectives.shouldNotReachHere("Unexpected socket option length in getsockopt"); + } + if (actualLen > 0) { + NativeMemory.readByteArrayElements(nativeOptval, 0, optval, 0, actualLen); + } + return actualLen; + } finally { + NativeMemory.free(nativeBufLen); + NativeMemory.free(nativeOptval); + } + } + + @ExportMessage + public void setsockopt(int sockfd, int level, int optname, byte[] optval, int optlen) throws PosixException { + assert optlen >= 0 && optval.length >= optlen; + long nativeOptval = NULLPTR; + try { + nativeOptval = NativeMemory.copyToNativeByteArrayOrNull(optval, 0, optlen); + int res = posixNativeFunctionInvoker.call_setsockopt(sockfd, level, optname, nativeOptval, optlen); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(nativeOptval); + } + } + + @ExportMessage + public int inet_addr(Object src) { + long srcPtr = pathToNativeCString(src); + try { + return posixNativeFunctionInvoker.call_inet_addr(srcPtr); + } finally { + NativeMemory.free(srcPtr); + } + } + + @ExportMessage + public int inet_aton(Object src) throws InvalidAddressException { + long srcPtr = pathToNativeCString(src); + try { + long r = posixNativeFunctionInvoker.call_inet_aton(srcPtr); + if (r < 0) { + throw new InvalidAddressException(); + } + return (int) r; + } finally { + NativeMemory.free(srcPtr); + } + } + + @ExportMessage + public Object inet_ntoa(int src) { + Buffer buf = Buffer.allocate(INET_ADDRSTRLEN.value); + long nativeBuf = NativeMemory.mallocByteArray(INET_ADDRSTRLEN.value); + try { + int len = posixNativeFunctionInvoker.call_inet_ntoa(src, nativeBuf); + if (len > 0) { + NativeMemory.readByteArrayElements(nativeBuf, 0, buf.data, 0, len); + } + return buf.withLength(len); + } finally { + NativeMemory.free(nativeBuf); + } + } + + @ExportMessage + public byte[] inet_pton(int family, Object src) throws PosixException, InvalidAddressException { + byte[] buf = new byte[family == AF_INET.value ? 4 : 16]; + long srcPtr = NULLPTR; + long nativeBuf = NULLPTR; + try { + srcPtr = pathToNativeCString(src); + nativeBuf = NativeMemory.mallocByteArray(buf.length); + int res = posixNativeFunctionInvoker.call_inet_pton(family, srcPtr, nativeBuf); + // Rather unusually, the return value of 0 does not indicate success but is used by + // inet_pton to report invalid format of the address (without setting errno). + // Success is reported by returning 1. + if (res == 1) { + NativeMemory.readByteArrayElements(nativeBuf, 0, buf, 0, buf.length); + return buf; + } + if (res == 0) { + throw new InvalidAddressException(); + } + throw getErrnoAndThrowPosixException(); + } finally { + NativeMemory.free(nativeBuf); + NativeMemory.free(srcPtr); + } + } + + @ExportMessage + public Object inet_ntop(int family, byte[] src) throws PosixException { + if ((family == AF_INET.value && src.length < 4) || (family == AF_INET6.value && src.length < 16)) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException("Invalid length of IPv4/6 address"); + } + Buffer buf = Buffer.allocate(INET6_ADDRSTRLEN.value); + long nativeSrc = NULLPTR; + long nativeBuf = NULLPTR; + try { + nativeSrc = NativeMemory.copyToNativeByteArray(src); + nativeBuf = NativeMemory.mallocByteArray(INET6_ADDRSTRLEN.value); + int res = posixNativeFunctionInvoker.call_inet_ntop(family, nativeSrc, nativeBuf, INET6_ADDRSTRLEN.value); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuf, 0, buf.data, 0, buf.data.length); + return buf.withLength(findZero(buf.data)); + } finally { + NativeMemory.free(nativeBuf); + NativeMemory.free(nativeSrc); + } + } + + @ExportMessage + public Object gethostname() throws PosixException { + int maxLen = (HOST_NAME_MAX.defined ? HOST_NAME_MAX.getValueIfDefined() : _POSIX_HOST_NAME_MAX.value) + 1; + Buffer buf = Buffer.allocate(maxLen); + long nativeBuf = NativeMemory.mallocByteArray(maxLen); + try { + int res = posixNativeFunctionInvoker.call_gethostname(nativeBuf, maxLen); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuf, 0, buf.data, 0, buf.data.length); + return buf.withLength(findZero(buf.data)); + } finally { + NativeMemory.free(nativeBuf); + } + } + + @ExportMessage + public Object[] getnameinfo(UniversalSockAddr usa, int flags, + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws GetAddrInfoException { + Buffer host = Buffer.allocate(NI_MAXHOST.value); + Buffer serv = Buffer.allocate(NI_MAXSERV.value); + UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; + long nativeAddr = NULLPTR; + long nativeHost = NULLPTR; + long nativeServ = NULLPTR; + try { + nativeAddr = NativeMemory.copyToNativeByteArray(addr.data, 0, addr.getLen()); + nativeHost = NativeMemory.mallocByteArray(NI_MAXHOST.value); + nativeServ = NativeMemory.mallocByteArray(NI_MAXSERV.value); + int res = posixNativeFunctionInvoker.call_getnameinfo(nativeAddr, addr.getLen(), nativeHost, NI_MAXHOST.value, nativeServ, NI_MAXSERV.value, flags); + if (res != 0) { + throw new GetAddrInfoException(res, gai_strerror(inliningTarget, res, zeroTerminatedUtf8ToTruffleStringNode)); + } + NativeMemory.readByteArrayElements(nativeHost, 0, host.data, 0, host.data.length); + NativeMemory.readByteArrayElements(nativeServ, 0, serv.data, 0, serv.data.length); + return new Object[]{ + host.withLength(findZero(host.data)), + serv.withLength(findZero(serv.data)), + }; + } finally { + NativeMemory.free(nativeServ); + NativeMemory.free(nativeHost); + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public AddrInfoCursor getaddrinfo(Object node, Object service, int family, int sockType, int protocol, int flags, + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws GetAddrInfoException { + long nodePtr = NULLPTR; + long servicePtr = NULLPTR; + long nativePtr = NULLPTR; + try { + nodePtr = pathToNativeCStringOrNull(node); + servicePtr = pathToNativeCStringOrNull(service); + nativePtr = NativeMemory.mallocLongArray(1); + int res = posixNativeFunctionInvoker.call_getaddrinfo(nodePtr, servicePtr, family, sockType, protocol, flags, nativePtr); + if (res != 0) { + throw new GetAddrInfoException(res, gai_strerror(inliningTarget, res, zeroTerminatedUtf8ToTruffleStringNode)); + } + long head = NativeMemory.readLong(nativePtr); + assert head != 0; // getaddrinfo should return at least one result + return new AddrInfoCursorImpl(this, head); + } finally { + NativeMemory.free(nativePtr); + NativeMemory.free(servicePtr); + NativeMemory.free(nodePtr); + } + } + + @ExportMessage + public TruffleString crypt(TruffleString word, TruffleString salt, + @Bind Node raisingNode, + @Exclusive @Cached TruffleString.SwitchEncodingNode switchEncodingToUtf8Node, + @Exclusive @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode, + @Exclusive @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws PosixException { + /* + * From the manpage: Upon successful completion, crypt returns a pointer to a string which + * encodes both the hashed passphrase, and the settings that were used to encode it. See + * crypt(5) for more detail on the format of hashed passphrases. crypt places its result in + * a static storage area, which will be overwritten by subsequent calls to crypt. It is not + * safe to call crypt from multiple threads simultaneously. Upon error, it may return a NULL + * pointer or a pointer to an invalid hash, depending on the implementation. + */ + long wordPtr = NULLPTR; + long saltPtr = NULLPTR; + try { + wordPtr = stringToNativeUTF8CString(word, switchEncodingToUtf8Node, copyToByteArrayNode); + saltPtr = stringToNativeUTF8CString(salt, switchEncodingToUtf8Node, copyToByteArrayNode); + // Note GIL is not enough as crypt is using global memory, we need a really global lock + synchronized (CRYPT_LOCK) { + long resultPtr; + try { + resultPtr = cryptNativeFunctionInvoker.crypt(wordPtr, saltPtr); + } catch (UnsupportedOperationException e) { + // Thrown by the generated invoker when CryptNativeFunction.loadNativeLibrary + // fails during its lazy library initialization path. + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw PRaiseNode.raiseStatic(raisingNode, PythonBuiltinClassType.SystemError, ErrorMessages.UNABLE_TO_LOAD_LIBCRYPT); + } + // CPython doesn't handle the case of "invalid hash" return specially and neither do + // we + if (resultPtr == 0) { + throw getErrnoAndThrowPosixException(); + } + return zeroTerminatedUtf8ToTruffleStringNode.execute(raisingNode, resultPtr); + } + } finally { + if (wordPtr != NULLPTR) { + NativeMemory.free(wordPtr); + } + if (saltPtr != NULLPTR) { + NativeMemory.free(saltPtr); + } + } + } + + private TruffleString gai_strerror(Node inliningTarget, int errorCode, NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) { + long nativeBuf = NativeMemory.mallocByteArray(1024); + try { + posixNativeFunctionInvoker.call_gai_strerror(errorCode, nativeBuf, 1024); + // TODO PyUnicode_DecodeLocale + return zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, nativeBuf); + } finally { + NativeMemory.free(nativeBuf); + } + } + + /** + * Provides access to {@code struct addrinfo}. + * + * The layout of native {@code struct addrinfo} is as follows: + * + *

      +     * {@code
      +     *     struct addrinfo {
      +     *         int              ai_flags;           // intData[0]
      +     *         int              ai_family;          // intData[1]
      +     *         int              ai_socktype;        // intData[2]
      +     *         int              ai_protocol;        // intData[3]
      +     *         socklen_t        ai_addrlen;         // intData[4]
      +     *         struct sockaddr *ai_addr;            // data copied into socketAddress[]
      +     *         char            *ai_canonname;       // longData[0]
      +     *         struct addrinfo *ai_next;            // longData[1]
      +     *     };
      +     * }
      +     * 
      + * + * To avoid multiple native calls, we transfer the data in batch using arrays of {@code int}s and + * {@code long}s - int values are stored in {@code intData}, the {@code ai_canonname} and + * {@code ai_next} pointers are stored in {@code longData} and the socket address pointed to by + * {@code ai_addr} is copied into Java byte array {@code socketAddress}. We also cache two + * additional integers: + *
        + *
      • {@code intData[5]} contains {@code ai_addr->sa_family},
      • + *
      • {@code intData[6]} contains the length of {@code ai_canonname} if it is not {@code null} + *
      • + *
      + * + * It is not clear whether it is guaranteed that {@code ai_family} and + * {@code ai_addr->sa_family} are always the same. We provide both and use the later when + * decoding the socket address. + */ + private static class AddrInfo { + private final int[] intData = new int[7]; + private final long[] longData = new long[2]; + private byte[] socketAddress; + + private void update(long ptr, NativePosixSupport nativePosixSupport) { + socketAddress = new byte[(int) nativePosixSupport.getConstant(SIZEOF_STRUCT_SOCKADDR_STORAGE)]; + long nativeIntData = NULLPTR; + long nativeLongData = NULLPTR; + long nativeSocketAddress = NULLPTR; + try { + nativeIntData = NativeMemory.mallocIntArray(intData.length); + nativeLongData = NativeMemory.mallocLongArray(longData.length); + nativeSocketAddress = NativeMemory.mallocByteArray(socketAddress.length); + int res = nativePosixSupport.posixNativeFunctionInvoker.get_addrinfo_members(ptr, nativeIntData, nativeLongData, nativeSocketAddress); + if (res != 0) { + throw shouldNotReachHere("the length of ai_canonname does not fit into an int"); + } + NativeMemory.readIntArrayElements(nativeIntData, 0, intData, 0, intData.length); + NativeMemory.readLongArrayElements(nativeLongData, 0, longData, 0, longData.length); + NativeMemory.readByteArrayElements(nativeSocketAddress, 0, socketAddress, 0, socketAddress.length); + } finally { + NativeMemory.free(nativeSocketAddress); + NativeMemory.free(nativeLongData); + NativeMemory.free(nativeIntData); + } + } + + int getFlags() { + return intData[0]; + } + + int getFamily() { + return intData[1]; + } + + int getSockType() { + return intData[2]; + } + + int getProtocol() { + return intData[3]; + } + + int getAddrLen() { + return intData[4]; + } + + int getAddrFamily() { + return intData[5]; + } + + int getCanonNameLen() { + assert getCanonNamePtr() != 0; + return intData[6]; + } + + long getCanonNamePtr() { + return longData[0]; + } + + long getNextPtr() { + return longData[1]; + } + } + + @ExportLibrary(AddrInfoCursorLibrary.class) + protected static class AddrInfoCursorImpl implements AddrInfoCursor { + + private final NativePosixSupport nativePosixSupport; + private long head; + private AddrInfo info; + + AddrInfoCursorImpl(NativePosixSupport nativePosixSupport, long head) { + this.nativePosixSupport = nativePosixSupport; + this.head = head; + info = new AddrInfo(); + info.update(head, nativePosixSupport); + } + + @ExportMessage + void release() { + checkReleased(); + nativePosixSupport.posixNativeFunctionInvoker.call_freeaddrinfo(head); + head = 0; + } + + @ExportMessage + boolean next() { + checkReleased(); + long nextPtr = info.getNextPtr(); + if (nextPtr == 0) { + return false; + } + info.update(nextPtr, nativePosixSupport); + return true; + } + + @ExportMessage + int getFlags() { + checkReleased(); + return info.getFlags(); + } + + @ExportMessage + int getFamily() { + checkReleased(); + return info.getFamily(); + } + + @ExportMessage + int getSockType() { + checkReleased(); + return info.getSockType(); + } + + @ExportMessage + int getProtocol() { + checkReleased(); + return info.getProtocol(); + } + + @ExportMessage + Object getCanonName() { + checkReleased(); + long namePtr = info.getCanonNamePtr(); + if (namePtr == 0) { + return null; + } + int nameLen = info.getCanonNameLen(); + byte[] buf = new byte[nameLen]; + UNSAFE.copyMemory(null, namePtr, buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, nameLen); + return Buffer.wrap(buf); + } + + @ExportMessage + UniversalSockAddr getSockAddr() { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(nativePosixSupport); + PythonUtils.arraycopy(info.socketAddress, 0, addr.data, 0, info.getAddrLen()); + addr.setFamily(info.getAddrFamily()); + addr.setLen(info.getAddrLen()); + return addr; + } + + private void checkReleased() { + if (head == 0) { + throw shouldNotReachHere("AddrInfoCursor has already been released"); + } + } + } + + @ExportMessage + UniversalSockAddr createUniversalSockAddrInet4(Inet4SockAddr src) { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + addr.setFamily(AF_INET.value); + ARRAY_ACCESSOR_BE.putShort(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT), (short) src.getPort()); + ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR) + getConstant(OFFSETOF_STRUCT_IN_ADDR_S_ADDR), src.getAddress()); + addr.setLen((int) getConstant(SIZEOF_STRUCT_SOCKADDR_IN)); + return addr; + } + + @ExportMessage + UniversalSockAddr createUniversalSockAddrInet6(Inet6SockAddr src) { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + addr.setFamily(AF_INET6.value); + ARRAY_ACCESSOR_BE.putShort(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT), (short) src.getPort()); + int addrOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR) + (int) getConstant(OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR); + PythonUtils.arraycopy(src.getAddress(), 0, addr.data, addrOffset, 16); + ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO), src.getFlowInfo()); + ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID), src.getScopeId()); + addr.setLen((int) getConstant(SIZEOF_STRUCT_SOCKADDR_IN6)); + return addr; + } + + @ExportMessage + UniversalSockAddr createUniversalSockAddrUnix(UnixSockAddr src) throws InvalidUnixSocketPathException { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + addr.setFamily(AF_UNIX.value); + byte[] path = src.getPath(); + if (path.length > getConstant(SIZEOF_STRUCT_SOCKADDR_UN_SUN_PATH)) { + throw InvalidUnixSocketPathException.INSTANCE; + } + int len = path.length + (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH); + PythonUtils.arraycopy(path, 0, addr.data, (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH), path.length); + addr.setLen(len); + return addr; + } + + @ExportLibrary(UniversalSockAddrLibrary.class) + protected static class UniversalSockAddrImpl implements UniversalSockAddr { + + private final NativePosixSupport nativePosixSupport; + private final byte[] data; + private int len = 0; + + UniversalSockAddrImpl(NativePosixSupport nativePosixSupport) { + this.nativePosixSupport = nativePosixSupport; + this.data = new byte[(int) getConstant(SIZEOF_STRUCT_SOCKADDR_STORAGE)]; + } + + @ExportMessage + int getFamily() { + int offset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY); + int size = (int) getConstant(SIZEOF_STRUCT_SOCKADDR_SA_FAMILY); + if (getLen() >= offset + size) { + if (size == 1) { + return Byte.toUnsignedInt(data[offset]); + } else if (size == 2) { + return Short.toUnsignedInt(ARRAY_ACCESSOR.getShort(data, offset)); + } else if (size == 4) { + return ARRAY_ACCESSOR.getInt(data, offset); + } else { + throw CompilerDirectives.shouldNotReachHere("Unexpected sizeof(sa_family_t)"); + } + } else { + return AF_UNSPEC.value; + } + } + + void setFamily(int family) { + int offset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY); + int size = (int) getConstant(SIZEOF_STRUCT_SOCKADDR_SA_FAMILY); + if (size == 1) { + data[offset] = (byte) family; + } else if (size == 2) { + ARRAY_ACCESSOR.putShort(data, offset, (short) family); + } else if (size == 4) { + ARRAY_ACCESSOR.putInt(data, offset, family); + } else { + throw CompilerDirectives.shouldNotReachHere("Unexpected sizeof(sa_family_t)"); + } + } + + @ExportMessage + Inet4SockAddr asInet4SockAddr() { + if (getFamily() != AF_INET.value) { + throw CompilerDirectives.shouldNotReachHere("Only AF_INET socket address can be converted to Inet4SockAddr"); + } + if (getLen() != getConstant(SIZEOF_STRUCT_SOCKADDR_IN)) { + throw CompilerDirectives.shouldNotReachHere("Wrong size of socket addr struct in asInet4SockAddr"); + } + int port = Short.toUnsignedInt(ARRAY_ACCESSOR_BE.getShort(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT))); + int address = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR) + getConstant(OFFSETOF_STRUCT_IN_ADDR_S_ADDR)); + return new Inet4SockAddr(port, address); + } + + @ExportMessage + Inet6SockAddr asInet6SockAddr() { + if (getFamily() != AF_INET6.value) { + throw CompilerDirectives.shouldNotReachHere("Only AF_INET6 socket address can be converted to Inet6SockAddr"); + } + if (getLen() != getConstant(SIZEOF_STRUCT_SOCKADDR_IN6)) { + throw CompilerDirectives.shouldNotReachHere("Wrong size of socket addr struct in asInet6SockAddr"); + } + int port = Short.toUnsignedInt(ARRAY_ACCESSOR_BE.getShort(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT))); + int addrOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR) + (int) getConstant(OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR); + byte[] address = PythonUtils.arrayCopyOfRange(data, addrOffset, addrOffset + 16); + int flowInfo = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO)); + int scopeId = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)); + return new Inet6SockAddr(port, address, flowInfo, scopeId); + } + + @ExportMessage + UnixSockAddr asUnixSockAddr() { + if (getFamily() != AF_UNIX.value) { + throw CompilerDirectives.shouldNotReachHere("Only AF_UNIX socket address can be converted to UnixSockAddr"); + } + int pathOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH); + byte[] pathBuf; + int linuxAddrLen = getLen() - pathOffset; + if (linuxAddrLen > 0 && data[pathOffset] == '\0') { + // Abstract Linux address + pathBuf = PythonUtils.arrayCopyOfRange(data, pathOffset, pathOffset + linuxAddrLen); + } else { + // Regular NULL-terminated string + int pathLen = ArrayUtils.indexOf(data, pathOffset, data.length, (byte) 0) - pathOffset; + assert pathLen >= 0; + pathBuf = PythonUtils.arrayCopyOfRange(data, pathOffset, pathOffset + pathLen); + } + return new UnixSockAddr(pathBuf); + } + + long getConstant(NativePosixConstants constant) { + return nativePosixSupport.getConstant(constant); + } + + int getLen() { + return len; + } + + void setLen(int len) { + this.len = len; + } + + } + + @ExportMessage + long semOpen(Object name, int openFlags, int mode, int value) throws PosixException { + long namePtr = pathToNativeCString(name); + try { + long ptr = posixNativeFunctionInvoker.call_sem_open(namePtr, openFlags, mode, value); + if (ptr == NULLPTR) { + throw getErrnoAndThrowPosixException(); + } + return ptr; + } finally { + NativeMemory.free(namePtr); + } + } + + @ExportMessage + void semClose(long handle) throws PosixException { + int res = posixNativeFunctionInvoker.call_sem_close(handle); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + void semUnlink(Object name) throws PosixException { + long namePtr = pathToNativeCString(name); + try { + int res = posixNativeFunctionInvoker.call_sem_unlink(namePtr); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(namePtr); + } + } + + private static final UnsupportedPosixFeatureException NO_SEM_GETVALUE_EXCEPTION = new UnsupportedPosixFeatureException("sem_getvalue is not available on the current platform"); + + @ExportMessage + int semGetValue(long handle) throws PosixException { + /* + * msimacek: It works on Linux, and it doesn't work on Darwin. It might work on some other + * Unix-likes, but it's hard to check, so let's assume it only works on Linux for now + */ + if (PythonLanguage.getPythonOS() != PythonOS.PLATFORM_LINUX) { + throw NO_SEM_GETVALUE_EXCEPTION; + } + long nativeValue = NativeMemory.mallocIntArray(1); + try { + int res = posixNativeFunctionInvoker.call_sem_getvalue(handle, nativeValue); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return NativeMemory.readInt(nativeValue); + } finally { + NativeMemory.free(nativeValue); + } + } + + @ExportMessage + void semPost(long handle) throws PosixException { + int res = posixNativeFunctionInvoker.call_sem_post(handle); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + void semWait(long handle) throws PosixException { + int res = posixNativeFunctionInvoker.call_sem_wait(handle); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + boolean semTryWait(long handle) throws PosixException { + int res = posixNativeFunctionInvoker.call_sem_trywait(handle); + if (res < 0) { + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno == OSErrorEnum.EAGAIN.getNumber()) { + return false; + } + throw newPosixException(errno); + } + return true; + } + + @ExportMessage + boolean semTimedWait(long handle, long deadlineNs, + @Bind Node node, + @CachedLibrary("this") PosixSupportLibrary thisLib) throws PosixException { + if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_LINUX) { + int res = posixNativeFunctionInvoker.call_sem_timedwait(handle, deadlineNs); + if (res < 0) { + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno == OSErrorEnum.ETIMEDOUT.getNumber()) { + return false; + } + throw newPosixException(errno); + } + return true; + } else { + long deadlineMs = deadlineNs / 1_000_000; + while (true) { + if (thisLib.semTryWait(this, handle)) { + return true; + } + long currentMs = System.currentTimeMillis(); + if (currentMs > deadlineMs) { + return false; + } + long delayMs = Math.min(deadlineMs - currentMs, 20); + TruffleSafepoint.setBlockedThreadInterruptible(node, Thread::sleep, delayMs); + } + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public PwdResult getpwuid(long uid, + @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + return getpw(uid, NULLPTR, fromByteArrayNode, switchEncodingFromUtf8Node); + } + + @ExportMessage + @SuppressWarnings("static-method") + public PwdResult getpwnam(Object name, + @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + long namePtr = pathToNativeCString(name); + try { + return getpw(-1, namePtr, fromByteArrayNode, switchEncodingFromUtf8Node); + } finally { + NativeMemory.free(namePtr); + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public boolean hasGetpwentries() { + return true; + } + + @ExportMessage + @SuppressWarnings("static-method") + public PwdResult[] getpwentries( + @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + // Note: this is not thread safe, so potentially problematic while running multiple contexts + // within one VM + int sysConfMax = getSysConfPwdSizeMax(); + int initialBufferSize = sysConfMax == -1 ? 1024 : sysConfMax; + + ArrayList result = new ArrayList<>(); + posixNativeFunctionInvoker.call_setpwent(); + long nativeBufferSize = NULLPTR; + long nativeOutput = NULLPTR; + int currentBufferSize = initialBufferSize; + long nativeBuffer = NULLPTR; + try { + nativeBufferSize = NativeMemory.mallocLongArray(1); + nativeOutput = NativeMemory.mallocLongArray(PWD_OUTPUT_LEN); + nativeBuffer = NativeMemory.mallocByteArray(currentBufferSize); + while (true) { + long pwPtr = posixNativeFunctionInvoker.call_getpwent(nativeBufferSize); + if (pwPtr == NULLPTR) { + break; + } + long bufferSize = NativeMemory.readLong(nativeBufferSize); + if (bufferSize < 0 || bufferSize > PWD_BUFFER_MAX_SIZE) { + throw outOfMemoryPosixError(); + } + if (currentBufferSize < bufferSize) { + NativeMemory.free(nativeBuffer); + currentBufferSize = (int) bufferSize; + nativeBuffer = NativeMemory.mallocByteArray(currentBufferSize); + } + int code = posixNativeFunctionInvoker.get_getpwent_data(pwPtr, nativeBuffer, currentBufferSize, nativeOutput); + if (code != 0) { + throw CompilerDirectives.shouldNotReachHere("get_getpwent_data failed"); + } + byte[] buffer = new byte[currentBufferSize]; + NativeMemory.readByteArrayElements(nativeBuffer, 0, buffer, 0, buffer.length); + long[] output = new long[PWD_OUTPUT_LEN]; + NativeMemory.readLongArrayElements(nativeOutput, 0, output, 0, output.length); + result.add(createPwdResult(buffer, output, fromByteArrayNode, switchEncodingFromUtf8Node)); + } + } finally { + posixNativeFunctionInvoker.call_endpwent(); + NativeMemory.free(nativeBuffer); + NativeMemory.free(nativeOutput); + NativeMemory.free(nativeBufferSize); + } + return toPwdResultArray(result); + } + + @TruffleBoundary + private static PwdResult[] toPwdResultArray(ArrayList result) { + return result.toArray(new PwdResult[0]); + } + + private PwdResult getpw(long uid, long namePtr, TruffleString.FromByteArrayNode fromByteArrayNode, + TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + int sysConfMax = getSysConfPwdSizeMax(); + int bufferSize = sysConfMax == -1 ? 1024 : sysConfMax; + while (bufferSize < PWD_BUFFER_MAX_SIZE) { + long nativeData = NULLPTR; + long nativeOutput = NULLPTR; + try { + nativeData = NativeMemory.mallocByteArray(bufferSize); + nativeOutput = NativeMemory.mallocLongArray(PWD_OUTPUT_LEN); + int result = namePtr == NULLPTR + ? posixNativeFunctionInvoker.call_getpwuid_r(uid, nativeData, bufferSize, nativeOutput) + : posixNativeFunctionInvoker.call_getpwname_r(namePtr, nativeData, bufferSize, nativeOutput); + if (result == -1) { + return null; + } + if (result == 0) { + byte[] data = new byte[bufferSize]; + NativeMemory.readByteArrayElements(nativeData, 0, data, 0, data.length); + long[] output = new long[PWD_OUTPUT_LEN]; + NativeMemory.readLongArrayElements(nativeOutput, 0, output, 0, output.length); + return createPwdResult(data, output, fromByteArrayNode, switchEncodingFromUtf8Node); + } + if (result != OSErrorEnum.ERANGE.getNumber() || sysConfMax != -1) { + // no point in trying larger buffer if we got different error or the OS already + // told + // us that sysConfMax should be enough... + throw newPosixException(result); + } + } finally { + NativeMemory.free(nativeOutput); + NativeMemory.free(nativeData); + } + bufferSize <<= 1; + } + throw outOfMemoryPosixError(); + } + + private static PwdResult createPwdResult(byte[] data, long[] output, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) + throws PosixException { + return new PwdResult( + extractZeroTerminatedString(data, output[0], fromByteArrayNode, switchEncodingFromUtf8Node), + output[1], output[2], + extractZeroTerminatedString(data, output[3], fromByteArrayNode, switchEncodingFromUtf8Node), + extractZeroTerminatedString(data, output[4], fromByteArrayNode, switchEncodingFromUtf8Node)); + } + + @ExportMessage + public int ioctlBytes(int fd, long request, byte[] arg) throws PosixException { + long nativeArg = NULLPTR; + try { + nativeArg = NativeMemory.mallocByteArray(Math.max(arg.length, 1)); + if (arg.length > 0) { + NativeMemory.writeByteArrayElements(nativeArg, 0, arg, 0, arg.length); + } + int res = posixNativeFunctionInvoker.call_ioctl_bytes(fd, request, nativeArg); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + if (arg.length > 0) { + NativeMemory.readByteArrayElements(nativeArg, 0, arg, 0, arg.length); + } + return res; + } finally { + NativeMemory.free(nativeArg); + } + } + + @ExportMessage + public int ioctlInt(int fd, long request, int arg) throws PosixException { + int res = posixNativeFunctionInvoker.call_ioctl_int(fd, request, arg); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + private static TruffleString extractZeroTerminatedString(byte[] buffer, long longOffset, TruffleString.FromByteArrayNode fromByteArrayNode, + TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + if (longOffset < 0 || longOffset >= buffer.length) { + throw outOfMemoryPosixError(); + } + int offset = (int) longOffset; + int end = ArrayUtils.indexOf(buffer, offset, buffer.length, (byte) 0); + if (end < 0) { + throw CompilerDirectives.shouldNotReachHere("Could not find the end of the string"); + } + // TODO PyUnicode_DecodeFSDefault + return createString(buffer, offset, end - offset, true, fromByteArrayNode, switchEncodingFromUtf8Node); + } + + private static PosixException outOfMemoryPosixError() throws PosixException { + throw new PosixErrnoException(OSErrorEnum.ENOMEM.getNumber(), OSErrorEnum.ENOMEM.getMessage()); + } + + private int sysConfPwdSizeMax = -1; + + private int getSysConfPwdSizeMax() throws PosixException { + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, sysConfPwdSizeMax == -1)) { + long sysConfMaxLong = posixNativeFunctionInvoker.get_sysconf_getpw_r_size_max(); + if (sysConfMaxLong != -1 && (sysConfMaxLong < 0 || sysConfMaxLong > PWD_BUFFER_MAX_SIZE)) { + throw outOfMemoryPosixError(); + } + sysConfPwdSizeMax = (int) sysConfMaxLong; + } + return sysConfPwdSizeMax; + } + + // ------------------ + // Path conversions + + @ExportMessage + @SuppressWarnings("static-method") + public Object createPathFromString(TruffleString path, + @Exclusive @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Exclusive @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { + TruffleString utf8 = switchEncodingNode.execute(path, UTF_8, SURROGATE_ESCAPE_TO_UTF8_TRANSCODING_ERROR_HANDLER); + return checkPath(copyToByteArrayNode.execute(utf8, UTF_8)); + } + + @ExportMessage + @SuppressWarnings("static-method") + public Object createPathFromBytes(byte[] path) { + return checkPath(path); + } + + @ExportMessage + @SuppressWarnings("static-method") + public TruffleString getPathAsString(Object path, + @Exclusive @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Exclusive @Cached TruffleString.IsValidNode isValidNode, + @Exclusive @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { + Buffer result = (Buffer) path; + if (result.length > Integer.MAX_VALUE) { + // sanity check that it is safe to cast result.length to int, to be removed once + // we support large arrays + throw CompilerDirectives.shouldNotReachHere("Posix path cannot fit into a Java array"); + } + int length = (int) result.length; + TruffleString utf8 = fromByteArrayNode.execute(result.data, 0, length, UTF_8, true); + if (isValidNode.execute(utf8, UTF_8)) { + return switchEncodingNode.execute(utf8, TS_ENCODING); + } + return switchEncodingNode.execute(utf8, TS_ENCODING, PyUnicodeFSDecoderNode.SURROGATE_ESCAPE_FROM_UTF8_TRANSCODING_ERROR_HANDLER); + } + + @ExportMessage + @SuppressWarnings("static-method") + public Buffer getPathAsBytes(Object path) { + return (Buffer) path; + } + + private static TruffleString createString(byte[] src, int offset, int length, boolean copy, TruffleString.FromByteArrayNode fromByteArrayNode, + TruffleString.SwitchEncodingNode switchEncodingNode) { + TruffleString utf8 = fromByteArrayNode.execute(src, offset, length, UTF_8, copy); + return switchEncodingNode.execute(utf8, TS_ENCODING); + } + + private static byte[] getStringBytes(TruffleString str, TruffleString.SwitchEncodingNode switchEncodingNode, TruffleString.CopyToByteArrayNode copyToByteArrayNode) { + TruffleString utf8 = switchEncodingNode.execute(str, UTF_8); + byte[] bytes = new byte[utf8.byteLength(UTF_8)]; + copyToByteArrayNode.execute(utf8, 0, bytes, 0, bytes.length, UTF_8); + return bytes; + } + + private static Buffer checkPath(byte[] path) { + for (byte b : path) { + if (b == 0) { + return null; + } + } + // TODO we keep a byte[] provided by the caller, who can potentially change it, making our + // check for embedded nulls pointless. Maybe we should copy it and while on it, might as + // well add the terminating null character, avoiding the copy we do later in pathToCString. + return Buffer.wrap(path); + } + + // ------------------ + // Objects/handles/pointers + + protected static class DirEntry { + final Buffer name; + final long ino; + final int type; + + DirEntry(Buffer name, long ino, int type) { + this.name = name; + this.ino = ino; + this.type = type; + } + + @Override + public String toString() { + return "DirEntry{" + + "name='" + new String(name.data, 0, (int) name.length) + "'" + + ", ino=" + ino + + ", type=" + type + + '}'; + } + } + + // ------------------ + // Helpers + + private PosixException getErrnoAndThrowPosixException() throws PosixException { + throw newPosixException(posixNativeFunctionInvoker.get_errno()); + } + + @TruffleBoundary + private PosixException newPosixException(int errno) throws PosixException { + throw new PosixErrnoException(errno, strerror(errno, null, NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode.getUncached())); + } + + private static long copyTimevalArrayToNativeOrNull(Timeval[] timeval) { + return timeval == null ? NULLPTR : NativeMemory.copyToNativeLongArray(new long[]{timeval[0].getSeconds(), timeval[0].getMicroseconds(), timeval[1].getSeconds(), timeval[1].getMicroseconds()}); + } + + private static long wrapItimerval(Timeval delay, Timeval interval) { + long ptr = NativeMemory.mallocLongArray(4); + NativeMemory.writeLongArrayElement(ptr, 0, delay.getSeconds()); + NativeMemory.writeLongArrayElement(ptr, 1, delay.getMicroseconds()); + NativeMemory.writeLongArrayElement(ptr, 2, interval.getSeconds()); + NativeMemory.writeLongArrayElement(ptr, 3, interval.getMicroseconds()); + return ptr; + } + + private static void readNativeSockAddr(long nativeAddr, long nativeAddrLen, UniversalSockAddrImpl addr) { + int addrLen = NativeMemory.readInt(nativeAddrLen); + if (addrLen < 0 || addrLen > addr.data.length) { + throw CompilerDirectives.shouldNotReachHere("Unexpected socket address length"); + } + addr.setLen(addrLen); + if (addrLen > 0) { + NativeMemory.readByteArrayElements(nativeAddr, 0, addr.data, 0, addrLen); + } + } + + private static Timeval[] unwrapTimeval(long nativeTimeval) { + return new Timeval[]{ + new Timeval(NativeMemory.readLongArrayElement(nativeTimeval, 0), NativeMemory.readLongArrayElement(nativeTimeval, 1)), + new Timeval(NativeMemory.readLongArrayElement(nativeTimeval, 2), NativeMemory.readLongArrayElement(nativeTimeval, 3)) + }; + } + + private static int findZero(byte[] buf) { + for (int i = 0; i < buf.length; ++i) { + if (buf[i] == 0) { + return i; + } + } + return buf.length; + } + + private long pathToNativeCStringOrNull(Object path) { + return path == null ? NULLPTR : bufferToNativeCString((Buffer) path); + } + + private long pathToNativeCString(Object path) { + return bufferToNativeCString((Buffer) path); + } + + private static long bufferToNativeCString(Buffer path) { + return NativeMemory.copyToNativeZeroTerminatedByteArray(path.data, 0, (int) path.length); + } + + private long stringToNativeUTF8CString(TruffleString input, + @Cached TruffleString.SwitchEncodingNode switchEncodingToUtf8Node, + @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { + byte[] utf8 = getStringBytes(input, switchEncodingToUtf8Node, copyToByteArrayNode); + return NativeMemory.copyToNativeZeroTerminatedByteArray(utf8, 0, utf8.length); + } + + private static void checkBounds(byte[] buf, int offset, int length) { + if (length < 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException(); + } + if (offset < 0 || offset + length > buf.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IndexOutOfBoundsException(); + } + } + + @TruffleBoundary + private static void log(Level level, String fmt, Object... args) { + if (LOGGER.isLoggable(level)) { + LOGGER.log(level, String.format(fmt, args)); + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeZlibSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeZlibSupport.java new file mode 100644 index 0000000000..99eaf06566 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativeZlibSupport.java @@ -0,0 +1,440 @@ +/* + * 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.runtime; + +import static com.oracle.graal.python.annotations.NativeSimpleType.DOUBLE; +import static com.oracle.graal.python.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT32; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT64; +import static com.oracle.graal.python.annotations.NativeSimpleType.VOID; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.annotations.DowncallSignature; +import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode; +import com.oracle.truffle.api.ThreadLocalAction.Access; +import com.oracle.truffle.api.TruffleLogger; +import com.oracle.truffle.api.strings.TruffleString; + +public class NativeZlibSupport extends NativeCompressionSupport { + + private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NativeZlibSupport.class); + private static final String SUPPORTING_NATIVE_LIB_NAME = "zsupport"; + + public static final int NO_ERROR = 0; + public static final int DEFLATE_INIT_ERROR = 101; + public static final int DEFLATE_END_ERROR = 102; + public static final int DEFLATE_DICT_ERROR = 103; + public static final int DEFLATE_OBJ_ERROR = 104; + public static final int DEFLATE_FLUSH_ERROR = 105; + public static final int DEFLATE_COPY_ERROR = 106; + public static final int DEFLATE_ERROR = 107; + public static final int INFLATE_INIT_ERROR = 201; + public static final int INFLATE_END_ERROR = 202; + public static final int INFLATE_DICT_ERROR = 203; + public static final int INFLATE_OBJ_ERROR = 204; + public static final int INFLATE_FLUSH_ERROR = 205; + public static final int INFLATE_COPY_ERROR = 206; + public static final int INFLATE_ERROR = 207; + public static final int INCOMPLETE_ERROR = 99; + public static final int MEMORY_ERROR = 999; + public static final int OUTPUT_OPTION = 0; + public static final int UNUSED_DATA_OPTION = 1; + public static final int UNCONSUMED_TAIL_OPTION = 2; + public static final int ZDICT_OPTION = 3; + + abstract static class ZlibNativeFunctions { + @DowncallSignature(returnType = POINTER) + abstract long zlib_get_version(); + + @DowncallSignature(returnType = POINTER) + abstract long zlib_get_runtime_version(); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64, POINTER, SINT32}) + abstract long zlib_crc32(long crc, long buf, int len); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64, POINTER, SINT32}) + abstract long zlib_adler32(long crc, long buf, int len); + + @DowncallSignature(returnType = POINTER) + abstract long zlib_create_zlib_stream(); + + @DowncallSignature(returnType = DOUBLE, argumentTypes = {POINTER}) + abstract double zlib_get_timeElapsed(long zst); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER}) + abstract void zlib_free_stream(long zst); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER}) + abstract void zlib_gc_helper(long zst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int zlib_get_error_type(long zst); + + @DowncallSignature(returnType = POINTER, argumentTypes = {POINTER}) + abstract long zlib_get_stream_msg(long zst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int zlib_has_stream_msg(long zst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int zlib_get_eof(long zst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int zlib_get_is_initialised(long zst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32}) + abstract int zlib_get_buffer_size(long zst, int option); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER, SINT32, POINTER}) + abstract void zlib_get_off_heap_buffer(long zst, int option, long dest); + + @DowncallSignature(returnType = POINTER) + abstract long zlib_create_compobject(); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT64, SINT32, SINT32}) + abstract int zlib_deflate_off_heap(long zst, long in, long inLen, long bufSize, int level, int wbits); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT64, SINT32}) + abstract int zlib_inflate_off_heap(long zst, long in, long inLen, long bufSize, int wbits); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, SINT32, SINT32, SINT32, SINT32, POINTER, SINT64}) + abstract int zlib_Compress_init(long zst, int level, int method, int wbits, int memLevel, int strategy, long dict, long dictLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, SINT32, SINT32, SINT32, SINT32}) + abstract int zlib_Compress_init_no_dict(long zst, int level, int method, int wbits, int memLevel, int strategy); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT64}) + abstract int zlib_Compress_obj(long zst, long in, long inLen, long bufSize); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT32}) + abstract int zlib_Compress_flush(long zst, long in, long bufSize, int mode); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int zlib_Compress_copy(long zst, long newCopy); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, POINTER, SINT64}) + abstract int zlib_Decompress_init(long zst, int wbits, long dict, long dictLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32}) + abstract int zlib_Decompress_init_no_dict(long zst, int wbits); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT64, SINT64}) + abstract int zlib_Decompress_obj(long zst, long in, long inLen, long bufSize, long maxLength); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int zlib_Decompress_flush(long zst, long length); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int zlib_Decompress_copy(long zst, long newCopy); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT64, SINT64}) + abstract int zlib_decompress(long zst, long data, long len, long maxLength); + + static NativeLibrary loadNativeLibrary(PythonContext context) { + return NativeCompressionSupport.loadNativeLibrary(context, SUPPORTING_NATIVE_LIB_NAME); + } + } + + private final ZlibNativeFunctions nativeFunctions; + + private NativeZlibSupport(PythonContext context) { + super(context); + this.nativeFunctions = isAvailable() ? new ZlibNativeFunctionsGen(context) : null; + } + + public static NativeZlibSupport createNative(PythonContext context, String noNativeAccessHelp) { + return new NativeZlibSupport(context); + } + + static class PointerReleaseCallback implements AsyncHandler.AsyncAction { + private final Pointer pointer; + + public PointerReleaseCallback(Pointer pointer) { + this.pointer = pointer; + } + + @Override + public void execute(PythonContext context, Access access) { + if (!pointer.markReleased()) { + assert pointer.isReleased(); + return; + } + try { + pointer.doRelease(); + LOGGER.finest("NativeZlibSupport pointer has been freed"); + } catch (Exception e) { + LOGGER.severe("Error while trying to free NativeZlibSupport pointer: " + e.getMessage()); + } + } + } + + public static class Pointer extends AsyncHandler.SharedFinalizer.FinalizableReference { + + private final NativeZlibSupport lib; + private final long pointer; + + public Pointer(Object referent, long pointer, NativeZlibSupport lib) { + super(referent, lib.pythonContext.getSharedFinalizer()); + this.lib = lib; + this.pointer = pointer; + } + + public long getPointer() { + return pointer; + } + + protected void doRelease() { + lib.gcReleaseHelper(pointer); + } + + @Override + public AsyncHandler.AsyncAction release() { + if (!isReleased()) { + return new PointerReleaseCallback(this); + } + return null; + } + } + + public TruffleString zlibVersion() { + return ZeroTerminatedUtf8ToTruffleStringNode.executeUncached(nativeFunctions.zlib_get_version()); + } + + public TruffleString zlibRuntimeVersion() { + return ZeroTerminatedUtf8ToTruffleStringNode.executeUncached(nativeFunctions.zlib_get_runtime_version()); + } + + public Object getTimeElapsed(long zst) { + return nativeFunctions.zlib_get_timeElapsed(zst); + } + + public Object gcReleaseHelper(long zst) { + nativeFunctions.zlib_gc_helper(zst); + return null; + } + + public long crc32(long crc, byte[] buf, int len) { + if (len == 0) { + return crc; + } + long nativeBuf = copyToNativeByteArray(buf, len); + try { + return nativeFunctions.zlib_crc32(crc, nativeBuf, len); + } finally { + if (nativeBuf != NativeMemory.NULLPTR) { + NativeMemory.free(nativeBuf); + } + } + } + + public long adler32(long crc, byte[] buf, int len) { + if (len == 0) { + return crc; + } + long nativeBuf = copyToNativeByteArray(buf, len); + try { + return nativeFunctions.zlib_adler32(crc, nativeBuf, len); + } finally { + if (nativeBuf != NativeMemory.NULLPTR) { + NativeMemory.free(nativeBuf); + } + } + } + + public long createStream() { + return nativeFunctions.zlib_create_zlib_stream(); + } + + public void deallocateStream(long zst) { + nativeFunctions.zlib_free_stream(zst); + } + + public int getErrorFunction(long zst) { + return nativeFunctions.zlib_get_error_type(zst); + } + + public TruffleString getStreamErrorMsg(long zst) { + return ZeroTerminatedUtf8ToTruffleStringNode.executeUncached(nativeFunctions.zlib_get_stream_msg(zst)); + } + + public int hasStreamErrorMsg(long zst) { + return nativeFunctions.zlib_has_stream_msg(zst); + } + + public int getEOF(long zst) { + return nativeFunctions.zlib_get_eof(zst); + } + + public int getIsInitialised(long zst) { + return nativeFunctions.zlib_get_is_initialised(zst); + } + + public int getBufferSize(long zst, int option) { + return nativeFunctions.zlib_get_buffer_size(zst, option); + } + + public void getBuffer(long zst, int option, byte[] dest) { + if (dest.length == 0) { + return; + } + long nativeDest = NativeMemory.mallocByteArray(dest.length); + try { + nativeFunctions.zlib_get_off_heap_buffer(zst, option, nativeDest); + NativeMemory.readByteArrayElements(nativeDest, 0, dest, 0, dest.length); + } finally { + NativeMemory.free(nativeDest); + } + } + + public long createCompObject() { + return nativeFunctions.zlib_create_compobject(); + } + + public int deflateOffHeap(long zst, byte[] in, long inLen, long bufSize, int level, int wbits) { + long nativeIn = copyToNativeByteArray(in, (int) inLen); + try { + return nativeFunctions.zlib_deflate_off_heap(zst, nativeIn, inLen, bufSize, level, wbits); + } finally { + if (nativeIn != NativeMemory.NULLPTR) { + NativeMemory.free(nativeIn); + } + } + } + + public int inflateOffHeap(long zst, byte[] in, long inLen, long bufSize, int wbits) { + long nativeIn = copyToNativeByteArray(in, (int) inLen); + try { + return nativeFunctions.zlib_inflate_off_heap(zst, nativeIn, inLen, bufSize, wbits); + } finally { + if (nativeIn != NativeMemory.NULLPTR) { + NativeMemory.free(nativeIn); + } + } + } + + public int compressObjInitWithDict(long zst, int level, int method, int wbits, int memLevel, int strategy, byte[] dict, long dictLen) { + long nativeDict = copyToNativeByteArray(dict, (int) dictLen); + try { + return nativeFunctions.zlib_Compress_init(zst, level, method, wbits, memLevel, strategy, nativeDict, dictLen); + } finally { + if (nativeDict != NativeMemory.NULLPTR) { + NativeMemory.free(nativeDict); + } + } + } + + public int compressObjInit(long zst, int level, int method, int wbits, int memLevel, int strategy) { + return nativeFunctions.zlib_Compress_init_no_dict(zst, level, method, wbits, memLevel, strategy); + } + + public int compressObj(long zst, Object in, long inLen, long bufSize) { + long nativeIn = copyToNativeByteArray((byte[]) in, (int) inLen); + try { + return nativeFunctions.zlib_Compress_obj(zst, nativeIn, inLen, bufSize); + } finally { + if (nativeIn != NativeMemory.NULLPTR) { + NativeMemory.free(nativeIn); + } + } + } + + public int compressObjFlush(long zst, byte[] in, long bufSize, int mode) { + long nativeIn = copyToNativeByteArray(in); + try { + return nativeFunctions.zlib_Compress_flush(zst, nativeIn, bufSize, mode); + } finally { + if (nativeIn != NativeMemory.NULLPTR) { + NativeMemory.free(nativeIn); + } + } + } + + public int compressObjCopy(long zst, long newCopy) { + return nativeFunctions.zlib_Compress_copy(zst, newCopy); + } + + public int decompressObjInitWithDict(long zst, int wbits, byte[] dict, long dictLen) { + long nativeDict = copyToNativeByteArray(dict, (int) dictLen); + try { + return nativeFunctions.zlib_Decompress_init(zst, wbits, nativeDict, dictLen); + } finally { + if (nativeDict != NativeMemory.NULLPTR) { + NativeMemory.free(nativeDict); + } + } + } + + public int decompressObjInit(long zst, int wbits) { + return nativeFunctions.zlib_Decompress_init_no_dict(zst, wbits); + } + + public int decompressObj(long zst, byte[] in, long inLen, long bufSize, long maxLength) { + long nativeIn = copyToNativeByteArray(in, (int) inLen); + try { + return nativeFunctions.zlib_Decompress_obj(zst, nativeIn, inLen, bufSize, maxLength); + } finally { + if (nativeIn != NativeMemory.NULLPTR) { + NativeMemory.free(nativeIn); + } + } + } + + public int decompressObjFlush(long zst, long length) { + return nativeFunctions.zlib_Decompress_flush(zst, length); + } + + public int decompressObjCopy(long zst, long newCopy) { + return nativeFunctions.zlib_Decompress_copy(zst, newCopy); + } + + public int decompressor(long zst, byte[] data, long len, long maxLength) { + long nativeData = copyToNativeByteArray(data, (int) len); + try { + return nativeFunctions.zlib_decompress(zst, nativeData, len, maxLength); + } finally { + if (nativeData != NativeMemory.NULLPTR) { + NativeMemory.free(nativeData); + } + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.java index dd8ce720cf..91a913a6d2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstants.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 @@ -150,7 +150,7 @@ public final class PosixConstants { public static final MandatoryIntConstant S_IFCHR; public static final MandatoryIntConstant S_IFIFO; public static final MandatoryIntConstant MAP_SHARED; - public static final MandatoryIntConstant MAP_PRIVATE; + public static final OptionalIntConstant MAP_PRIVATE; public static final MandatoryIntConstant MAP_ANONYMOUS; public static final OptionalIntConstant MAP_DENYWRITE; public static final OptionalIntConstant MAP_EXECUTABLE; @@ -483,7 +483,7 @@ public final class PosixConstants { S_IFCHR = reg.createMandatoryInt("S_IFCHR"); S_IFIFO = reg.createMandatoryInt("S_IFIFO"); MAP_SHARED = reg.createMandatoryInt("MAP_SHARED"); - MAP_PRIVATE = reg.createMandatoryInt("MAP_PRIVATE"); + MAP_PRIVATE = reg.createOptionalInt("MAP_PRIVATE"); MAP_ANONYMOUS = reg.createMandatoryInt("MAP_ANONYMOUS"); MAP_DENYWRITE = reg.createOptionalInt("MAP_DENYWRITE"); MAP_EXECUTABLE = reg.createOptionalInt("MAP_EXECUTABLE"); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsWin32.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsWin32.java index 1ce31c2371..7233591050 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsWin32.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixConstantsWin32.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 @@ -85,7 +85,6 @@ static void getConstants(PosixConstants.Registry constants) { constants.put("S_IFCHR", 0x00002000); constants.put("S_IFIFO", 0); constants.put("MAP_SHARED", 1); - constants.put("MAP_PRIVATE", 2); constants.put("MAP_ANONYMOUS", 4); constants.put("PROT_NONE", 0); constants.put("PROT_READ", 1); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java index 54e6e80290..728dbf8770 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.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 @@ -54,6 +54,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; +import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -246,7 +247,7 @@ public abstract class PosixSupportLibrary extends Library { public abstract void renameat(Object receiver, int oldDirFd, Object oldPath, int newDirFd, Object newPath) throws PosixException; - public abstract boolean faccessat(Object receiver, int dirFd, Object path, int mode, boolean effectiveIds, boolean followSymlinks); + public abstract boolean faccessat(Object receiver, int dirFd, Object path, int mode, boolean effectiveIds, boolean followSymlinks) throws UnsupportedPosixFeatureException; public abstract void fchmodat(Object receiver, int dirFd, Object path, int mode, boolean followSymlinks) throws PosixException; @@ -258,6 +259,16 @@ public abstract class PosixSupportLibrary extends Library { public abstract Object readlinkat(Object receiver, int dirFd, Object path) throws PosixException; + public abstract void raise(Object receiver, int signal) throws PosixException; + + public abstract int alarm(Object receiver, int seconds) throws PosixException; + + public abstract Timeval[] getitimer(Object receiver, int which) throws PosixException; + + public abstract Timeval[] setitimer(Object receiver, int which, Timeval delay, Timeval interval) throws PosixException; + + public abstract void signalSelf(Object receiver, int signal) throws PosixException; + public abstract void kill(Object receiver, long pid, int signal) throws PosixException; public abstract void killpg(Object receiver, long pid, int signal) throws PosixException; @@ -282,19 +293,19 @@ public abstract class PosixSupportLibrary extends Library { public abstract long getuid(Object receiver); - public abstract long geteuid(Object receiver); + public abstract long geteuid(Object receiver) throws UnsupportedPosixFeatureException; public abstract long getgid(Object receiver); - public abstract long getegid(Object receiver); + public abstract long getegid(Object receiver) throws UnsupportedPosixFeatureException; - public abstract long getppid(Object receiver); + public abstract long getppid(Object receiver) throws UnsupportedPosixFeatureException; public abstract long getpgid(Object receiver, long pid) throws PosixException; public abstract void setpgid(Object receiver, long pid, long pgid) throws PosixException; - public abstract long getpgrp(Object receiver); + public abstract long getpgrp(Object receiver) throws UnsupportedPosixFeatureException; public abstract long getsid(Object receiver, long pid) throws PosixException; @@ -337,14 +348,15 @@ public record OpenPtyResult(int masterFd, int slaveFd) { public abstract TruffleString ctermid(Object receiver) throws PosixException; - // note: this leaks memory in nfi backend and is not synchronized + // note: this leaks memory in native backend and is not synchronized // TODO is it worth synchronizing at least all accesses made through PosixSupportLibrary? public abstract void setenv(Object receiver, Object name, Object value, boolean overwrite) throws PosixException; public abstract void unsetenv(Object receiver, Object name) throws PosixException; public abstract int forkExec(Object receiver, Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, - int stderrReadFd, int stderrWriteFd, int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep) throws PosixException; + int stderrReadFd, int stderrWriteFd, int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, + boolean allowVFork) throws PosixException; // args.length must be > 0 public abstract void execv(Object receiver, Object pathname, Object[] args) throws PosixException; @@ -366,7 +378,7 @@ public abstract int forkExec(Object receiver, Object[] executables, Object[] arg public abstract void mmapUnmap(Object receiver, Object mmap, long length) throws PosixException; - public abstract long mmapGetPointer(Object receiver, Object mmap); + public abstract long mmapGetPointer(Object receiver, Object mmap) throws UnsupportedPosixFeatureException; public abstract long semOpen(Object receiver, Object name, int openFlags, int mode, int value) throws PosixException; @@ -799,7 +811,7 @@ public String toString() { * getnameinfo uses its own error codes and gai_strerror instead of the usual errno * and strerror) */ - public abstract Object[] getnameinfo(Object receiver, UniversalSockAddr addr, int flags) throws GetAddrInfoException; + public abstract Object[] getnameinfo(Object receiver, UniversalSockAddr addr, int flags) throws UnsupportedPosixFeatureException, GetAddrInfoException; /** * Corresponds to POSIX {@code getaddrinfo(3)}, except it always passes a non-null value for the @@ -822,7 +834,8 @@ public String toString() { * getaddrinfo uses its own error codes and gai_strerror instead of the usual errno * and strerror) */ - public abstract AddrInfoCursor getaddrinfo(Object receiver, Object node, Object service, int family, int sockType, int protocol, int flags) throws GetAddrInfoException; + public abstract AddrInfoCursor getaddrinfo(Object receiver, Object node, Object service, int family, int sockType, int protocol, int flags) + throws UnsupportedPosixFeatureException, GetAddrInfoException; /** * Represents one or more addrinfos returned by {@code getaddrinfo()}. @@ -910,7 +923,7 @@ public static AddrInfoCursorLibrary getUncached() { * Exception that indicates and error while executing * {@link #getaddrinfo(Object, Object, Object, int, int, int, int)}. */ - public static class GetAddrInfoException extends Exception { + public static final class GetAddrInfoException extends Exception { private static final long serialVersionUID = 3013253817849329391L; @@ -947,7 +960,7 @@ public final Throwable fillInStackTrace() { public abstract UniversalSockAddr createUniversalSockAddrInet6(Object receiver, Inet6SockAddr src); - public abstract UniversalSockAddr createUniversalSockAddrUnix(Object receiver, UnixSockAddr src) throws InvalidUnixSocketPathException; + public abstract UniversalSockAddr createUniversalSockAddrUnix(Object receiver, UnixSockAddr src) throws UnsupportedPosixFeatureException, InvalidUnixSocketPathException; /** * Provides messages for manipulating {@link UniversalSockAddr}. @@ -1002,19 +1015,44 @@ public static UniversalSockAddrLibrary getUncached() { } } + /** + * Base class for exceptions that originate in the POSIX support layer. + */ + public abstract static class PosixException extends Exception { + + private static final long serialVersionUID = 8700515065120346760L; + + protected PosixException() { + } + + protected PosixException(String message) { + super(message); + } + + public final boolean hasErrno(OSErrorEnum errno) { + return this instanceof PosixErrnoException errnoException && errnoException.getErrorCode() == errno.getNumber(); + } + + @SuppressWarnings("sync-override") + @Override + public Throwable fillInStackTrace() { + return this; + } + } + /** * Exception that indicates POSIX level error associated with numeric code. If the message is * known, it may be included in the exception, otherwise it can be queried using * {@link #strerror(Object, int)}. */ - public static final class PosixException extends Exception { + public static final class PosixErrnoException extends PosixException { private static final long serialVersionUID = -115762483478883093L; private final int errorCode; private final transient TruffleString msg; - public PosixException(int errorCode, TruffleString message) { + public PosixErrnoException(int errorCode, TruffleString message) { this.errorCode = errorCode; msg = message; } @@ -1031,12 +1069,6 @@ public String getMessage() { public int getErrorCode() { return errorCode; } - - @SuppressWarnings("sync-override") - @Override - public Throwable fillInStackTrace() { - return this; - } } /** @@ -1083,19 +1115,13 @@ public final Throwable fillInStackTrace() { * certain feature is supported or not, but even then this exception may be thrown for other * features. */ - public static class UnsupportedPosixFeatureException extends RuntimeException { + public static class UnsupportedPosixFeatureException extends PosixException { private static final long serialVersionUID = 1846254827094902593L; public UnsupportedPosixFeatureException(String message) { super(message); } - - @Override - @SuppressWarnings("sync-override") - public final Throwable fillInStackTrace() { - return this; - } } /** diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java index 518aefd17e..8df18530a9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.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,6 +62,7 @@ import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.library.CachedLibrary; @@ -548,7 +549,7 @@ final void renameat(int oldDirFd, Object oldPath, int newDirFd, Object newPath, @ExportMessage final boolean faccessat(int dirFd, Object path, int mode, boolean effectiveIds, boolean followSymlinks, - @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) { + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException { checkNotInPreInitialization(); return nativeLib.faccessat(nativePosixSupport, dirFd, path, mode, effectiveIds, followSymlinks); } @@ -588,6 +589,41 @@ final Object readlinkat(int dirFd, Object path, return nativeLib.readlinkat(nativePosixSupport, dirFd, path); } + @ExportMessage + final void raise(int signal, + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { + checkNotInPreInitialization(); + nativeLib.raise(nativePosixSupport, signal); + } + + @ExportMessage + final int alarm(int seconds, + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { + checkNotInPreInitialization(); + return nativeLib.alarm(nativePosixSupport, seconds); + } + + @ExportMessage + final Timeval[] getitimer(int which, + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { + checkNotInPreInitialization(); + return nativeLib.getitimer(nativePosixSupport, which); + } + + @ExportMessage + final Timeval[] setitimer(int which, Timeval delay, Timeval interval, + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { + checkNotInPreInitialization(); + return nativeLib.setitimer(nativePosixSupport, which, delay, interval); + } + + @ExportMessage + final void signalSelf(int signal, + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { + checkNotInPreInitialization(); + nativeLib.signalSelf(nativePosixSupport, signal); + } + @ExportMessage final void kill(long pid, int signal, @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { @@ -672,7 +708,7 @@ final long getuid(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary } @ExportMessage - final long geteuid(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) { + final long geteuid(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException { checkNotInPreInitialization(); return nativeLib.geteuid(nativePosixSupport); } @@ -684,13 +720,13 @@ final long getgid(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary } @ExportMessage - final long getegid(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) { + final long getegid(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException { checkNotInPreInitialization(); return nativeLib.getegid(nativePosixSupport); } @ExportMessage - final long getppid(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) { + final long getppid(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException { checkNotInPreInitialization(); return nativeLib.getppid(nativePosixSupport); } @@ -710,7 +746,7 @@ final void setpgid(long pid, long pgid, } @ExportMessage - final long getpgrp(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) { + final long getpgrp(@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException { checkNotInPreInitialization(); return nativeLib.getpgrp(nativePosixSupport); } @@ -771,11 +807,11 @@ final void unsetenv(Object name, @ExportMessage final int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd, - int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, + int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int pgidToSet, int[] fdsToKeep, boolean allowVFork, @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { checkNotInPreInitialization(); return nativeLib.forkExec(nativePosixSupport, executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd, stderrWriteFd, errPipeReadFd, errPipeWriteFd, - closeFds, restoreSignals, callSetsid, fdsToKeep); + closeFds, restoreSignals, callSetsid, pgidToSet, fdsToKeep, allowVFork); } @ExportMessage @@ -844,7 +880,7 @@ final void mmapUnmap(Object mmap, long length, @ExportMessage @SuppressWarnings("static-method") final long mmapGetPointer(Object mmap, - @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) { + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException { checkNotInPreInitialization(); return nativeLib.mmapGetPointer(nativePosixSupport, mmap); } @@ -1029,14 +1065,14 @@ final Object gethostname(@CachedLibrary("this.nativePosixSupport") PosixSupportL @ExportMessage final Object[] getnameinfo(UniversalSockAddr addr, int flags, - @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws GetAddrInfoException { + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException, GetAddrInfoException { checkNotInPreInitialization(); return nativeLib.getnameinfo(nativePosixSupport, addr, flags); } @ExportMessage final AddrInfoCursor getaddrinfo(Object node, Object service, int family, int sockType, int protocol, int flags, - @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws GetAddrInfoException { + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException, GetAddrInfoException { checkNotInPreInitialization(); return nativeLib.getaddrinfo(nativePosixSupport, node, service, family, sockType, protocol, flags); } @@ -1120,7 +1156,7 @@ final UniversalSockAddr createUniversalSockAddrInet6(Inet6SockAddr src, @ExportMessage final UniversalSockAddr createUniversalSockAddrUnix(UnixSockAddr src, - @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws InvalidUnixSocketPathException { + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws UnsupportedPosixFeatureException, InvalidUnixSocketPathException { checkNotInPreInitialization(); return nativeLib.createUniversalSockAddrUnix(nativePosixSupport, src); } 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 1b91e7d4d7..548eaa1012 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 @@ -30,12 +30,16 @@ import static com.oracle.graal.python.annotations.PythonOS.PLATFORM_DARWIN; import static com.oracle.graal.python.annotations.PythonOS.PLATFORM_WIN32; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError; +import static com.oracle.graal.python.builtins.modules.SysModuleBuiltins.T_ABIFLAGS; import static com.oracle.graal.python.builtins.modules.SysModuleBuiltins.T_CACHE_TAG; 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; import static com.oracle.graal.python.nodes.BuiltinNames.T_SHA3; import static com.oracle.graal.python.nodes.BuiltinNames.T_STDERR; import static com.oracle.graal.python.nodes.BuiltinNames.T_STDOUT; @@ -43,6 +47,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.T_THREADING; import static com.oracle.graal.python.nodes.BuiltinNames.T___BUILTINS__; import static com.oracle.graal.python.nodes.BuiltinNames.T___MAIN__; +import static com.oracle.graal.python.nodes.BuiltinNames.T___STDOUT__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___ANNOTATIONS__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___FILE__; import static com.oracle.graal.python.nodes.SpecialMethodNames.T_INSERT; @@ -50,7 +55,6 @@ import static com.oracle.graal.python.nodes.StringLiterals.J_EXT_DYLIB; import static com.oracle.graal.python.nodes.StringLiterals.J_EXT_SO; import static com.oracle.graal.python.nodes.StringLiterals.J_LIB_PREFIX; -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_DOT; import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; @@ -63,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; @@ -74,18 +79,20 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.nio.file.LinkOption; +import java.nio.file.Path; import java.security.NoSuchAlgorithmException; +import java.security.ProviderException; import java.security.SecureRandom; import java.text.MessageFormat; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Random; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; @@ -104,18 +111,17 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.PythonOS; import com.oracle.graal.python.builtins.Python3Core; -import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.MarshalModuleBuiltins; import com.oracle.graal.python.builtins.modules.MathGuards; 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.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; @@ -137,7 +143,6 @@ import com.oracle.graal.python.builtins.objects.thread.PLock; import com.oracle.graal.python.builtins.objects.thread.PThread; import com.oracle.graal.python.builtins.objects.tuple.PTuple; -import com.oracle.graal.python.compiler.CodeUnit; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectIsTrueNode; @@ -160,8 +165,12 @@ import com.oracle.graal.python.runtime.arrow.ArrowSupport; import com.oracle.graal.python.runtime.exception.ExceptionUtils; import com.oracle.graal.python.runtime.exception.PException; +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.NativeAccessSupport; +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; @@ -191,6 +200,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; @@ -198,8 +208,10 @@ import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; +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; import com.oracle.truffle.api.utilities.CyclicAssumption; import com.oracle.truffle.api.utilities.TriState; import com.oracle.truffle.api.utilities.TruffleWeakReference; @@ -208,19 +220,36 @@ @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. public boolean wasStackWalk; + public static boolean isCurrentThreadVirtual() { + return NativeAccessSupport.isCurrentThreadVirtual(); + } + @TruffleBoundary public static String getSupportLibName(PythonOS os, String libName) { // note: this should be aligned with MX's "lib" substitution @@ -241,6 +270,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); + long mem; + int byteLength = utf8String.byteLength(Encoding.UTF_8); + if (contextMemory) { + mem = allocateContextMemory(byteLength + 1); + NativeMemory.writeByte(mem + byteLength, (byte) 0); + } else { + mem = NativeMemory.callocByteArray(byteLength + 1); + } + utf8String.copyToNativeMemoryUncached(0, mem, 0, byteLength, Encoding.UTF_8); + return mem; + } + + 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 */ @@ -331,19 +393,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; @@ -450,12 +507,24 @@ public void setDict(PDict dict) { this.dict = dict; } - public PThreadState getNativeWrapper() { - return nativeWrapper; + public long getNativePointer() { + return nativePointer; + } + + public void setNativePointer(long pointer) { + assert this.nativePointer == UNINITIALIZED; + assert pointer != NATIVE_POINTER_FREED; + this.nativePointer = pointer; } - public void setNativeWrapper(PThreadState nativeWrapper) { - this.nativeWrapper = nativeWrapper; + public void clearNativePointer() { + assert this.nativePointer != NATIVE_POINTER_FREED; + this.nativePointer = NATIVE_POINTER_FREED; + } + + public void resetNativePointerAfterDetach() { + assert this.nativePointer != NATIVE_POINTER_FREED; + this.nativePointer = UNINITIALIZED; } public PContextVarsContext getContextVarsContext(Node node) { @@ -470,7 +539,7 @@ public void setContextVarsContext(PContextVarsContext contextVarsContext) { this.contextVarsContext = contextVarsContext; } - public void dispose(PythonContext context, boolean canRunGuestCode) { + public void dispose(boolean clearNativeThreadLocalVarPointer, boolean markShuttingDown) { // This method may be called twice on the same object. /* @@ -479,29 +548,34 @@ public void dispose(PythonContext context, boolean canRunGuestCode) { * 'CApiTransitions.pollReferenceQueue'. */ if (dict != null) { - PythonAbstractObjectNativeWrapper dictNativeWrapper = dict.getNativeWrapper(); - if (dictNativeWrapper != null && dictNativeWrapper.ref == null) { - CApiTransitions.releaseNativeWrapperUncached(dictNativeWrapper); + if (dict.isNative() && dict.ref == null) { + CApiTransitions.releaseNativeWrapper(dict.getNativePointer()); } + dict = null; } - dict = null; - if (nativeWrapper != null) { - if (nativeWrapper.ref == null) { - // There is no PythonObjectReference, this will not be collected anywhere else - CApiTransitions.releaseNativeWrapperUncached(nativeWrapper); - } - nativeWrapper = null; + + PThreadState.dispose(this, markShuttingDown); + if (!markShuttingDown) { + caughtException = null; + topframeref = null; + traceFun = null; + tracing = false; + profileFun = null; + profiling = false; + contextVarsContext = null; + runningEventLoop = null; + asyncgenFirstIter = null; + recursionDepth = 0; } + /* * 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. + * struct such that it cannot accidentally be reused. */ - if (nativeThreadLocalVarPointer != null && canRunGuestCode) { - CStructAccess.WritePointerNode.writeUncached(nativeThreadLocalVarPointer, 0, context.getNativeNull()); - nativeThreadLocalVarPointer = null; + if (nativeThreadLocalVarPointer != NULLPTR && clearNativeThreadLocalVarPointer) { + NativeMemory.writePtr(nativeThreadLocalVarPointer, NULLPTR); } + nativeThreadLocalVarPointer = NULLPTR; } public Object getTraceFun() { @@ -581,26 +655,33 @@ 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; } + + public Object getNativeThreadLocalVarPointer() { + return nativeThreadLocalVarPointer; + } + + public boolean isNativeThreadStateInitialized() { + return nativeThreadLocalVarPointer != NULLPTR; + } } private static final class AtExitHook { final Object callable; final Object[] arguments; final PKeyword[] keywords; - final CallTarget ct; + final RootNode rootNode; - AtExitHook(Object callable, Object[] arguments, PKeyword[] keywords, CallTarget ct) { + AtExitHook(Object callable, Object[] arguments, PKeyword[] keywords, RootNode rootNode) { this.callable = callable; this.arguments = arguments; this.keywords = keywords; - this.ct = ct; + this.rootNode = rootNode; } } @@ -608,6 +689,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(); } @@ -615,7 +701,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) { @@ -623,7 +709,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) { @@ -634,23 +720,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, @@ -660,7 +729,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(); @@ -669,7 +738,7 @@ PythonThreadState doGenericWithContext(Node inliningTarget, PythonContext contex } @NonIdempotent - PythonThreadState getThreadState(Node n) { + static PythonThreadState getThreadState(Node n) { return PythonLanguage.get(n).getThreadStateLocal().get(); } } @@ -687,20 +756,22 @@ PythonThreadState getThreadState(Node n) { private PythonModule mainModule; private final List shutdownHooks = new ArrayList<>(); private final List atExitHooks = new ArrayList<>(); + private final List auditHooks = new ArrayList<>(); private final List capiHooks = new ArrayList<>(); private final HashMap nativeClassStableAssumptions = new HashMap<>(); private final ThreadGroup threadGroup = new ThreadGroup(GRAALPYTHON_THREADS); private final IDUtils idUtils = new IDUtils(); @CompilationFinal private SecureRandom secureRandom; + private InitializationEntropySource initializationEntropySource; // Equivalent of _Py_HashSecret @CompilationFinal(dimensions = 1) private byte[] hashSecret = new byte[24]; @CompilationFinal private PosixSupport posixSupport; - @CompilationFinal private NFIZlibSupport nativeZlib; - @CompilationFinal private NFIBz2Support nativeBz2lib; - @CompilationFinal private NFILZMASupport nativeLZMA; + @CompilationFinal private NativeZlibSupport nativeZlib; + @CompilationFinal private NativeBz2Support nativeBz2lib; + @CompilationFinal private NativeLZMASupport nativeLZMA; // if set to 0 the VM will set it to whatever it likes private final AtomicLong pythonThreadStackSize = new AtomicLong(0); @@ -708,7 +779,7 @@ PythonThreadState getThreadState(Node n) { @CompilationFinal private TruffleLanguage.Env env; /* map of Python threads' IDs to the corresponding 'threadStates' */ - private final Map threadStateMapping = Collections.synchronizedMap(new WeakHashMap<>()); + private final Map threadStateMapping = new WeakHashMap<>(); private WeakReference mainThread; /* List of non-Python level threads. Those threads will be joined in finalizeContext. */ @@ -720,12 +791,23 @@ PythonThreadState getThreadState(Node n) { private OutputStream out; private OutputStream err; private InputStream in; + + public enum CApiState { + UNINITIALIZED, + INITIALIZING, + INITIALIZED, + FAILED, + CANNOT_IMPORT + } + + /** Initialization state of the C API context. */ + private volatile CApiState cApiState = CApiState.UNINITIALIZED; private final ReentrantLock cApiInitializationLock = new ReentrantLock(false); - private volatile boolean cApiWasInitialized = false; @CompilationFinal private CApiContext cApiContext; @CompilationFinal private boolean nativeAccessAllowed; + @CompilationFinal private NativeContext nativeContext; - private TruffleString soABI; // cache for soAPI + private TruffleString soABI; private static final class GlobalInterpreterLock extends ReentrantLock { private static final long serialVersionUID = 1L; @@ -764,36 +846,10 @@ public Thread getOwner() { @CompilationFinal(dimensions = 1) private Object[] optionValues; - /* - * These maps are used to ensure that each "deserialization" of code in the parser gets a - * different instance (inside one context - ASTs can still be shared between contexts). - * Deserializing the same code multiple times is an infrequent case, but Python assumes that - * these code instances don't share attributes like the associated filename. - * - * Each time a specific filename is passed to deserialization in the same context, it gets a new - * id. The filename is stored in a weak hash map, because the code itself is a - * context-independent object. - */ - private final WeakHashMap codeFilename = new WeakHashMap<>(); - - /* - * These maps are used to ensure that each "deserialization" of code in the parser gets a - * different instance (inside one context - ASTs can still be shared between contexts). - * Deserializing the same code multiple times is an infrequent case, but Python assumes that - * these code instances don't share attributes like the associated filename. - * - * Each time a specific filename is passed to deserialization in the same context, it gets a new - * id. The filename is stored in a weak hash map, because the code itself is a - * context-independent object. - */ - private final WeakHashMap codeUnitFilename = new WeakHashMap<>(); - - private final ConcurrentHashMap deserializationId = new ConcurrentHashMap<>(); - @CompilationFinal private long perfCounterStart = System.nanoTime(); public static final String CHILD_CONTEXT_DATA = "childContextData"; - @CompilationFinal private List childContextFDs; + @CompilationFinal private ArrayList childContextFDs; private final ChildContextData childContextData; private final SharedMultiprocessingData sharedMultiprocessingData; @@ -812,8 +868,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 @@ -1216,10 +1270,6 @@ public static PythonContext get(Node node) { return REFERENCE.get(node); } - public NativePointer getNativeNull() { - return nativeNull; - } - public boolean isChildContext() { return childContextData != null; } @@ -1265,7 +1315,7 @@ private static void start(Thread thread) { thread.start(); } - public synchronized List getChildContextFDs() { + public synchronized ArrayList getChildContextFDs() { if (childContextFDs == null) { childContextFDs = new ArrayList<>(); } @@ -1365,15 +1415,15 @@ public boolean isNativeAccessAllowed() { return nativeAccessAllowed; } - public NFIZlibSupport getNFIZlibSupport() { + public NativeZlibSupport getNativeZlibSupport() { return nativeZlib; } - public NFIBz2Support getNFIBz2Support() { + public NativeBz2Support getNativeBz2Support() { return nativeBz2lib; } - public NFILZMASupport getNFILZMASupport() { + public NativeLZMASupport getNativeLZMASupport() { return nativeLZMA; } @@ -1465,6 +1515,44 @@ public SecureRandom getSecureRandom() { return secureRandom; } + public void fillInitializationEntropyBytes(byte[] bytes) { + getInitializationEntropySource().nextBytes(bytes); + } + + private InitializationEntropySource getInitializationEntropySource() { + assert !env.isPreInitialization(); + if (initializationEntropySource == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + initializationEntropySource = createInitializationEntropySource(getOption(PythonOptions.InitializationEntropySource)); + } + return initializationEntropySource; + } + + private InitializationEntropySource createInitializationEntropySource(String spec) { + if ("default".equals(spec)) { + return new DefaultInitializationEntropySource(getSecureRandom()); + } + if (spec.startsWith("device:")) { + String path = spec.substring("device:".length()); + if (path.isEmpty()) { + throw new IllegalArgumentException("python.InitializationEntropySource device path must not be empty"); + } + return new DeviceInitializationEntropySource(path); + } + if (spec.startsWith("fixed:")) { + String seed = spec.substring("fixed:".length()); + if (seed.isEmpty()) { + throw new IllegalArgumentException("python.InitializationEntropySource fixed seed must not be empty"); + } + try { + return new FixedInitializationEntropySource(Long.decode(seed)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("python.InitializationEntropySource fixed seed must be a decimal or hexadecimal long", e); + } + } + throw new IllegalArgumentException("python.InitializationEntropySource must be 'default', 'device:', or 'fixed:'"); + } + public byte[] getHashSecret() { assert !env.isPreInitialization(); return hashSecret; @@ -1653,9 +1741,9 @@ private void setupRuntimeInformation(boolean isPatching) { initializeLocale(); setIntMaxStrDigits(getOption(PythonOptions.IntMaxStrDigits)); if (!PythonImageBuildOptions.WITHOUT_COMPRESSION_LIBRARIES) { - nativeZlib = NFIZlibSupport.createNative(this, ""); - nativeBz2lib = NFIBz2Support.createNative(this, ""); - nativeLZMA = NFILZMASupport.createNative(this, ""); + nativeZlib = NativeZlibSupport.createNative(this, ""); + nativeBz2lib = NativeBz2Support.createNative(this, ""); + nativeLZMA = NativeLZMASupport.createNative(this, ""); } mainModule = PFactory.createPythonModule(T___MAIN__); @@ -1697,7 +1785,65 @@ private void initializeHashSecret() { } } else { // Generate random seed - getSecureRandom().nextBytes(hashSecret); + fillInitializationEntropyBytes(hashSecret); + } + } + + private interface InitializationEntropySource { + void nextBytes(byte[] bytes); + } + + private static final class DefaultInitializationEntropySource implements InitializationEntropySource { + private final SecureRandom secureRandom; + + DefaultInitializationEntropySource(SecureRandom secureRandom) { + this.secureRandom = secureRandom; + } + + @Override + public synchronized void nextBytes(byte[] bytes) { + secureRandom.nextBytes(bytes); + } + } + + private static final class DeviceInitializationEntropySource implements InitializationEntropySource { + private final InputStream inputStream; + + DeviceInitializationEntropySource(String path) { + try { + inputStream = java.nio.file.Files.newInputStream(Path.of(path)); + } catch (IOException e) { + throw new IllegalArgumentException("failed to open initialization entropy device: " + path, e); + } + } + + @Override + public synchronized void nextBytes(byte[] bytes) { + int offset = 0; + try { + while (offset < bytes.length) { + int read = inputStream.read(bytes, offset, bytes.length - offset); + if (read < 0) { + throw new ProviderException("initialization entropy device exhausted"); + } + offset += read; + } + } catch (IOException e) { + throw new ProviderException("failed to read initialization entropy device", e); + } + } + } + + private static final class FixedInitializationEntropySource implements InitializationEntropySource { + private final Random random; + + FixedInitializationEntropySource(long seed) { + this.random = new Random(seed); + } + + @Override + public synchronized void nextBytes(byte[] bytes) { + random.nextBytes(bytes); } } @@ -1708,6 +1854,10 @@ public void applyModuleOptions() { if (!eqNode.execute(T_JAVA, sha3Backend, TS_ENCODING)) { removeBuiltinModule(T_SHA3); } + TruffleString pyexpatBackend = getLanguage().getEngineOption(PythonOptions.PyExpatModuleBackend); + if (!eqNode.execute(T_JAVA, pyexpatBackend, TS_ENCODING)) { + removeBuiltinModule(T_PYEXPAT); + } } private void initializePosixSupport() { @@ -1716,30 +1866,35 @@ private void initializePosixSupport() { // The resources field will be removed once all posix builtins go through PosixSupport TruffleString.EqualNode eqNode = TruffleString.EqualNode.getUncached(); boolean selectedJavaBackend = eqNode.execute(T_JAVA, option, TS_ENCODING); - if (PythonImageBuildOptions.WITHOUT_NATIVE_POSIX || selectedJavaBackend) { - if (!selectedJavaBackend) { - writeWarning("Native Posix backend selected, but it was excluded from the runtime, " + - "switching to Java backend."); - } + boolean selectedNativeBackend = eqNode.execute(T_NATIVE, option, TS_ENCODING); + if (selectedJavaBackend) { + result = new EmulatedPosixSupport(this); + } else if (selectedNativeBackend && PythonImageBuildOptions.WITHOUT_NATIVE_POSIX) { + writeWarning("Native Posix backend selected, but it was excluded from the runtime, " + + "switching to Java backend."); result = new EmulatedPosixSupport(this); - } else if (eqNode.execute(T_NATIVE, option, TS_ENCODING)) { + } else if (selectedNativeBackend && !NativeAccessSupport.isAvailable()) { + writeWarning("Native Posix backend selected, but native access downcalls are not available in this runtime, " + + "switching to Java backend."); + result = new EmulatedPosixSupport(this); + } else if (selectedNativeBackend) { if (env.isPreInitialization()) { EmulatedPosixSupport emulatedPosixSupport = new EmulatedPosixSupport(this); - NFIPosixSupport nativePosixSupport = new NFIPosixSupport(this, option); + NativePosixSupport nativePosixSupport = new NativePosixSupport(this, option); result = new PreInitPosixSupport(env, nativePosixSupport, emulatedPosixSupport); } else if (TruffleOptions.AOT) { // We always use a PreInitPosixSupport on SVM to keep the type of the posixSupport // field consistent for both pre-initialized and not-pre-initialized contexts so // that host inlining and PE see only one type, and also to avoid polymorphism when // calling library methods. - NFIPosixSupport nativePosixSupport = new NFIPosixSupport(this, option); + NativePosixSupport nativePosixSupport = new NativePosixSupport(this, option); result = new PreInitPosixSupport(env, nativePosixSupport, null); } else { if (!getOption(PythonOptions.RunViaLauncher)) { writeWarning("Native Posix backend is not fully supported when embedding. For example, standard I/O always uses file " + "descriptors 0, 1 and 2 regardless of stream redirection specified in Truffle environment"); } - result = new NFIPosixSupport(this, option); + result = new NativePosixSupport(this, option); } } else { throw new IllegalStateException(String.format("Wrong value for the PosixModuleBackend option: '%s'", option)); @@ -1972,8 +2127,8 @@ public void registerAtexitHook(ShutdownHook shutdownHook) { } @TruffleBoundary - public void registerAtexitHook(Object callable, Object[] arguments, PKeyword[] keywords, CallTarget ct) { - atExitHooks.add(new AtExitHook(callable, arguments, keywords, ct)); + public void registerAtexitHook(Object callable, Object[] arguments, PKeyword[] keywords, RootNode rootNode) { + atExitHooks.add(new AtExitHook(callable, arguments, keywords, rootNode)); } @TruffleBoundary @@ -1986,8 +2141,18 @@ public void clearAtexitHooks() { atExitHooks.clear(); } + @TruffleBoundary + public Object[] getAuditHooks() { + return auditHooks.toArray(PythonUtils.EMPTY_OBJECT_ARRAY); + } + + @TruffleBoundary + public void addAuditHook(Object hook) { + auditHooks.add(hook); + } + public void registerCApiHook(Runnable hook) { - if (hasCApiContext()) { + if (getCApiState() == CApiState.INITIALIZED) { hook.run(); } else { capiHooks.add(hook); @@ -1998,6 +2163,7 @@ public void registerCApiHook(Runnable hook) { @SuppressWarnings("try") public void finalizeContext() { boolean cancelling = env.getContext().isCancelling(); + boolean stdioFlushFailed = false; try (GilNode.UncachedAcquire gil = GilNode.uncachedAcquire()) { if (!cancelling) { // this uses the threading module and runs python code to join the threads @@ -2008,12 +2174,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(); - flushStdFiles(); - if (cApiContext != null) { - cApiContext.finalizeCApi(); + stdioFlushFailed = flushStdFiles(); + if (nativeContext != null) { + nativeContext.close(); } + freeContextMemory(); // destroy thread state data, if anything is still running, it will crash now disposeThreadStates(); } @@ -2025,17 +2195,24 @@ public void finalizeContext() { } } mainThread = null; + if (stdioFlushFailed) { + throw new PythonExitException(null, 120); + } } // Equivalent of CPython's flush_std_files @TruffleBoundary - public void flushStdFiles() { + public boolean flushStdFiles() { PythonModule sysModule = getSysModule(); - flushFile(sysModule.getAttribute(T_STDOUT), true); - flushFile(sysModule.getAttribute(T_STDERR), false); + Object stdout = sysModule.getAttribute(T_STDOUT); + return flushFile(stdout, sysModule.getAttribute(T___STDOUT__), true) | + flushFile(sysModule.getAttribute(T_STDERR), null, false); } - private static void flushFile(Object file, boolean useWriteUnraisable) { + private static final String SHUTDOWN_LOCK_ERROR_PREFIX = "could not acquire lock for "; + private static final String SHUTDOWN_LOCK_ERROR_SUFFIX = " at interpreter shutdown, possibly due to daemon threads"; + + private static boolean flushFile(Object file, Object originalStdout, boolean useWriteUnraisable) { if (!(file instanceof PNone)) { boolean closed = false; try { @@ -2048,11 +2225,20 @@ private static void flushFile(Object file, boolean useWriteUnraisable) { PyObjectCallMethodObjArgs.executeUncached(file, T_FLUSH); } catch (PException e) { if (useWriteUnraisable) { - WriteUnraisableNode.getUncached().execute(e.getEscapedException(), null, null); + if (!isDaemonThreadShutdownLockError(file, originalStdout, e)) { + WriteUnraisableNode.getUncached().execute(e.getEscapedException(), null, file); + return true; + } } } } } + return false; + } + + private static boolean isDaemonThreadShutdownLockError(Object file, Object originalStdout, PException e) { + String message = ExceptionUtils.getExceptionMessage(e.getUnreifiedException()); + return file == originalStdout && message != null && message.contains(SHUTDOWN_LOCK_ERROR_PREFIX) && message.endsWith(SHUTDOWN_LOCK_ERROR_SUFFIX); } @TruffleBoundary @@ -2067,7 +2253,7 @@ public void runAtexitHooks() { for (int i = atExitHooks.size() - 1; i >= 0; i--) { AtExitHook hook = atExitHooks.get(i); try { - hook.ct.call(hook.callable, hook.arguments, hook.keywords); + hook.rootNode.getCallTarget().call(hook.callable, hook.arguments, hook.keywords); } catch (PException e) { lastException = e; } @@ -2093,14 +2279,17 @@ public void runShutdownHooks() { /** * Release all resources held by the thread states. This function needs to run as long as the - * context is still valid because it may call into LLVM to release handles. + * context is still valid because it may release handles. */ @TruffleBoundary private void disposeThreadStates() { - for (PythonThreadState ts : threadStateMapping.values()) { - ts.dispose(this, true); + Thread currentThread = Thread.currentThread(); + synchronized (this) { + for (Map.Entry entry : threadStateMapping.entrySet()) { + entry.getValue().dispose(true, entry.getKey() == currentThread); + } + threadStateMapping.clear(); } - threadStateMapping.clear(); } /** @@ -2158,7 +2347,10 @@ private void joinPythonThreads() { // make a copy of the threads, because the threads will disappear one by one from the // threadStateMapping as we're joining them, which gives undefined results for the // iterator over keySet - LinkedList threads = new LinkedList<>(threadStateMapping.keySet()); + LinkedList threads; + synchronized (this) { + threads = new LinkedList<>(threadStateMapping.keySet()); + } boolean runViaLauncher = getOption(PythonOptions.RunViaLauncher); for (Thread thread : threads) { if (thread != Thread.currentThread()) { @@ -2488,8 +2680,8 @@ public boolean isPyFileInLanguageHome(TruffleFile path) { // This deliberately uses 'getAbsoluteFile' and not 'getCanonicalFile' because if, e.g., // 'path' is a symlink outside of the language home, the user should not be able to read // the symlink if 'allowIO' is false. - TruffleFile coreHomePath = getEnv().getInternalTruffleFile(langHome.toJavaStringUncached()).getAbsoluteFile(); - TruffleFile absolutePath = path.getAbsoluteFile(); + TruffleFile coreHomePath = getEnv().getInternalTruffleFile(langHome.toJavaStringUncached()).getAbsoluteFile().normalize(); + TruffleFile absolutePath = path.getAbsoluteFile().normalize(); return absolutePath.startsWith(coreHomePath); } LOGGER.log(Level.FINE, () -> "Cannot access file " + path + " because there is no language home."); @@ -2524,7 +2716,9 @@ public void popCurrentImport() { public Thread[] getThreads() { CompilerAsserts.neverPartOfCompilation(); - return threadStateMapping.keySet().toArray(new Thread[0]); + synchronized (this) { + return threadStateMapping.keySet().toArray(new Thread[0]); + } } public PythonThreadState getThreadState(PythonLanguage lang) { @@ -2556,6 +2750,10 @@ private void applyToAllThreadStates(Consumer action) { } } + public void clearNativeThreadStateSingletons() { + applyToAllThreadStates(PThreadState::clearSingletons); + } + @TruffleBoundary public void setSentinelLockWeakref(WeakReference sentinelLock) { getThreadState(getLanguage()).sentinelLock = sentinelLock; @@ -2566,22 +2764,94 @@ public void initializeMultiThreading() { handler.activateGIL(); } - public synchronized void attachThread(Thread thread, ContextThreadLocal threadState) { + public void attachThread(Thread thread, ContextThreadLocal threadState) { + CompilerAsserts.neverPartOfCompilation(); + PythonThreadState pythonThreadState = threadState.get(thread); + PythonThreadState previousThreadState; + synchronized (this) { + previousThreadState = threadStateMapping.put(thread, pythonThreadState); + } + ReentrantLock initLock = getcApiInitializationLock(); + /* + * Synchronize with C API initialization so that we do not miss eager initialization of this + * thread's 'tstate_current'. Otherwise, a thread could attach while another thread is + * sweeping all already-attached threads during C API initialization, observe + * 'INITIALIZING', skip eager initialization here, and then also miss the initialization + * sweep because it was not yet part of the thread snapshot. + */ + initLock.lock(); + try { + if (getCApiState() == CApiState.INITIALIZED) { + if (isCurrentThreadVirtual()) { + throw PRaiseNode.raiseStatic(getLanguage().unavailableSafepointLocation, SystemError, ErrorMessages.NATIVE_EXTENSIONS_VIRTUAL_THREAD); + } + // initialize this thread's native TLS slot eagerly instead of on first use + initializeNativeThreadState(pythonThreadState); + } + } catch (PException e) { + synchronized (this) { + if (previousThreadState == null) { + threadStateMapping.remove(thread); + } else { + threadStateMapping.put(thread, previousThreadState); + } + } + throw e; + } finally { + initLock.unlock(); + } + } + + @TruffleBoundary + public void initializeNativeThreadState() { + LOGGER.fine(() -> "Initializing native thread state for thread " + Thread.currentThread()); + 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(); - threadStateMapping.put(thread, threadState.get(thread)); + try (GilNode.UncachedAcquire ignored = GilNode.uncachedAcquire()) { + assert getCApiContext() != null; + 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); + } + } + + public void disposeThread(Thread thread, boolean canRunGuestCode) { + disposeThread(thread, canRunGuestCode, true); } - public synchronized void disposeThread(Thread thread, boolean canRunGuestCode) { + /** + * Disposes GraalPy state associated with {@code thread}. + * + * {@code markShuttingDown} distinguishes final thread exit from a temporary native-thread + * detach. Truffle's {@link ContextThreadLocal} does not expose a way to clear one language local + * for a still-alive thread, so a native thread that calls {@code PyGILState_Ensure} again after + * {@code PyGILState_Release} may receive the same Java {@link PythonThreadState} object. Such a + * detach must remove the thread from {@link #threadStateMapping} and free the native + * {@code PyThreadState}, but it must not mark the Java thread state as shutting down. + */ + public void disposeThread(Thread thread, boolean canRunGuestCode, boolean markShuttingDown) { CompilerAsserts.neverPartOfCompilation(); - // check if there is a live sentinel lock - PythonThreadState ts = threadStateMapping.get(thread); - if (ts == null) { - // ts already removed, that is valid during context shutdown for daemon threads - return; + PythonThreadState ts; + synchronized (this) { + // check if there is a live sentinel lock + ts = threadStateMapping.get(thread); + if (ts == null) { + // ts already removed, that is valid during context shutdown for daemon threads + return; + } + if (markShuttingDown) { + ts.shutdown(); + } + threadStateMapping.remove(thread); } - ts.shutdown(); - threadStateMapping.remove(thread); - ts.dispose(this, canRunGuestCode); + ts.dispose(thread == Thread.currentThread(), markShuttingDown); releaseSentinelLock(ts.sentinelLock); getSharedMultiprocessingData().removeChildContextThread(PThread.getThreadId(thread)); } @@ -2596,25 +2866,30 @@ private static void releaseSentinelLock(WeakReference sentinelLockWeakref } } - public boolean hasCApiContext() { - // This may be called during C API initialization, we have a context so that we can finish - // the initialization, but the C API is not fully initialized yet - assert (cApiContext != null) || !cApiWasInitialized; - return cApiContext != null; - } - - public boolean isCApiInitialized() { - assert (cApiContext != null) || !cApiWasInitialized; - return cApiWasInitialized; + public CApiState getCApiState() { + assert cApiContext != null || cApiState == CApiState.UNINITIALIZED || cApiState == CApiState.FAILED || cApiState == CApiState.CANNOT_IMPORT : cApiState; + return cApiState; } - public void setCApiInitialized() { - assert cApiContext != null; - cApiWasInitialized = true; + public void setCApiState(CApiState state) { + /*- Allowed transitions: + * 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 || 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) || !cApiWasInitialized; + assert cApiContext != null || cApiState == CApiState.UNINITIALIZED || cApiState == CApiState.FAILED || cApiState == CApiState.CANNOT_IMPORT; return cApiContext; } @@ -2624,9 +2899,19 @@ public ReentrantLock getcApiInitializationLock() { public void setCApiContext(CApiContext capiContext) { assert this.cApiContext == null : "tried to create new C API context but it was already created"; + assert getCApiState() == CApiState.UNINITIALIZED; 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(); @@ -2646,34 +2931,6 @@ public boolean isFinalizing() { return finalizing; } - public void setCodeFilename(CallTarget callTarget, TruffleString filename) { - assert PythonUtils.isInterned(filename); - codeFilename.put(callTarget, filename); - } - - public TruffleString getCodeFilename(CallTarget callTarget) { - return codeFilename.get(callTarget); - } - - public void setCodeUnitFilename(CodeUnit co, TruffleString filename) { - assert PythonUtils.isInterned(filename); - codeUnitFilename.put(co, filename); - } - - public TruffleString getCodeUnitFilename(CodeUnit co) { - return codeUnitFilename.get(co); - } - - public long getDeserializationId(TruffleString fileName) { - return deserializationId.computeIfAbsent(fileName, f -> new AtomicLong()).incrementAndGet(); - } - - public void ensureNFILanguage(Node nodeForRaise, String optionName, String optionValue) { - if (!env.getInternalLanguages().containsKey(J_NFI_LANGUAGE)) { - throw PRaiseNode.raiseStatic(nodeForRaise, PythonBuiltinClassType.SystemError, ErrorMessages.NFI_NOT_AVAILABLE, optionName, optionValue); - } - } - @TruffleBoundary public TruffleString getSoAbi() { if (soABI == null) { @@ -2681,6 +2938,7 @@ public TruffleString getSoAbi() { Object implementationObj = ReadAttributeFromModuleNode.getUncached().execute(sysModule, T_IMPLEMENTATION); // sys.implementation.cache_tag TruffleString cacheTag = (TruffleString) PyObjectGetAttr.executeUncached(implementationObj, T_CACHE_TAG); + TruffleString abiFlags = (TruffleString) ReadAttributeFromModuleNode.getUncached().execute(sysModule, T_ABIFLAGS); // sys.implementation._multiarch TruffleString multiArch = (TruffleString) PyObjectGetAttr.executeUncached(implementationObj, T__MULTIARCH); @@ -2696,7 +2954,7 @@ public TruffleString getSoAbi() { soExt = T_EXT_SO; } - soABI = cat(T_DOT, cacheTag, T_DASH, T_NATIVE, T_DASH, multiArch, soExt); + soABI = cat(T_DOT, cacheTag, abiFlags, T_DASH, T_NATIVE, T_DASH, multiArch, soExt); } return soABI; } @@ -2747,52 +3005,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 A native pointer. + */ @TruffleBoundary - private static void setNativeMemoryBoundary(Unsafe unsafe, long pointer, int size, byte value) { - unsafe.setMemory(pointer, size, value); + public long allocateContextMemory(int byteSize) { + ensureNativeAccess(); + if (nativeResources == null) { + nativeResources = new LinkedList<>(); + } + long nativePointer = NativeMemory.malloc(byteSize); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine(String.format("Allocated %d bytes of context memory: 0x%x", 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 (long nativePointer : nativeResources) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine(String.format("Freeing context memory: 0x%x", nativePointer)); + } + NativeMemory.free(nativePointer); + } + } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonCryptoFeature.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonCryptoFeature.java new file mode 100644 index 0000000000..8f0bb49409 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonCryptoFeature.java @@ -0,0 +1,104 @@ +/* + * 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 + * + * 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; + +import java.io.IOException; +import java.io.InputStream; +import java.security.Security; + +import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.hosted.RuntimeReflection; +import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; + +public final class PythonCryptoFeature implements Feature { + + private static final String[] HKDF_REFLECTIVE_CLASSES = { + "com.sun.crypto.provider.HKDFKeyDerivation$HKDFSHA256", + "com.sun.crypto.provider.HKDFKeyDerivation$HKDFSHA384", + "com.sun.crypto.provider.HKDFKeyDerivation$HKDFSHA512", + "sun.security.pkcs11.P11HKDF", + }; + private static final String BC_SUPPORT_CLASS = "com.oracle.graal.python.bouncycastle.BouncyCastleSupportImpl"; + private static final String BC_SUPPORT_SERVICE_RESOURCE = "META-INF/services/com.oracle.graal.python.runtime.crypto.BouncyCastleSupport"; + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + registerHkdfReflection(); + registerOptionalBouncyCastleSupport(); + } + + private static void registerHkdfReflection() { + for (String name : HKDF_REFLECTIVE_CLASSES) { + try { + Class.forName(name); + } catch (SecurityException | ClassNotFoundException e) { + return; + } + } + Security.addProvider(Security.getProvider("SunJCE")); + for (String name : HKDF_REFLECTIVE_CLASSES) { + try { + Class clazz = Class.forName(name); + RuntimeReflection.register(clazz); + RuntimeReflection.register(clazz.getConstructors()); + } catch (SecurityException | ClassNotFoundException e) { + throw new RuntimeException("Could not register " + name + " for reflective access!", e); + } + } + } + + private static void registerOptionalBouncyCastleSupport() { + try { + Class clazz = Class.forName(BC_SUPPORT_CLASS); + RuntimeReflection.register(clazz); + RuntimeReflection.register(clazz.getConstructors()); + try (InputStream stream = clazz.getClassLoader().getResourceAsStream(BC_SUPPORT_SERVICE_RESOURCE)) { + if (stream != null) { + RuntimeResourceAccess.addResource(clazz.getModule(), BC_SUPPORT_SERVICE_RESOURCE, stream.readAllBytes()); + } + } + } catch (ClassNotFoundException e) { + return; + } catch (SecurityException | IOException e) { + throw new RuntimeException("Could not register optional BouncyCastle support for native image.", e); + } + } +} 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 42d6177e6e..5fffa6fc26 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 @@ -62,6 +62,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Option; import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.bytecode.BytecodeNode; import com.oracle.truffle.api.dsl.Idempotent; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.nodes.ExplodeLoop; @@ -88,6 +89,8 @@ public final class PythonOptions { * bytecode interpreter. */ public static final boolean ENABLE_BYTECODE_DSL_INTERPRETER; + private static final OptionType TS_OPTION_TYPE = new OptionType<>("graal.python.TruffleString", PythonUtils::toTruffleStringUncached); + static { String prop = System.getProperty("python.EnableBytecodeDSLInterpreter"); if (prop != null) { @@ -106,8 +109,6 @@ public final class PythonOptions { } } - private static final OptionType TS_OPTION_TYPE = new OptionType<>("graal.python.TruffleString", PythonUtils::toTruffleStringUncached); - private PythonOptions() { // no instances } @@ -220,6 +221,11 @@ public static void checkBytecodeDSLEnv() { } })); + @Option(category = OptionCategory.EXPERT, help = "Entropy source used for reviewed initialization-time randomness. " + + "Use 'default' to reuse the default Java source, 'device:' to read bytes from a device or file, " + + "or 'fixed:' for deterministic testing.", usageSyntax = "default|device:|fixed:", stability = OptionStability.EXPERIMENTAL) // + public static final OptionKey InitializationEntropySource = new OptionKey<>("default"); + @EngineOption @Option(category = OptionCategory.USER, help = "Choose the backend for the POSIX module.", usageSyntax = "java|native", stability = OptionStability.STABLE) // public static final OptionKey PosixModuleBackend = new OptionKey<>(T_JAVA, TS_OPTION_TYPE); @@ -229,9 +235,18 @@ public static void checkBytecodeDSLEnv() { @EngineOption @Option(category = OptionCategory.USER, help = "Choose the backend for the Zlib, Bz2, and LZMA modules.", usageSyntax = "java|native", stability = OptionStability.STABLE) // public static final OptionKey CompressionModulesBackend = new OptionKey<>(T_JAVA, TS_OPTION_TYPE); + @EngineOption @Option(category = OptionCategory.USER, help = "Choose the backend for the pyexpat module.", usageSyntax = "java|native", stability = OptionStability.STABLE) // + public static final OptionKey PyExpatModuleBackend = new OptionKey<>(T_JAVA, TS_OPTION_TYPE); + + @EngineOption @Option(category = OptionCategory.USER, help = "Allow the unicodedata module to fall back from the ICU database to CPython's native UCD for unsupported features.", usageSyntax = "true|false", stability = OptionStability.STABLE) // + public static final OptionKey UnicodeCharacterDatabaseNativeFallback = new OptionKey<>(false); + @Option(category = OptionCategory.USER, help = "Install default signal handlers on startup", usageSyntax = "true|false", stability = OptionStability.STABLE) // public static final OptionKey InstallSignalHandlers = new OptionKey<>(false); + @Option(category = OptionCategory.USER, help = "Allow installing signal handlers", usageSyntax = "true|false", stability = OptionStability.STABLE) // + public static final OptionKey AllowSignalHandlers = new OptionKey<>(false); + @Option(category = OptionCategory.EXPERT, help = "Sets the language and territory, which will be used for initial locale. Format: 'language[_territory]', e.g., 'en_GB'. Leave empty to use the JVM default locale.", stability = OptionStability.STABLE) // public static final OptionKey InitialLocale = new OptionKey<>(""); @@ -366,8 +381,19 @@ public static void checkBytecodeDSLEnv() { @Option(category = OptionCategory.INTERNAL, usageSyntax = "