|
1 | 1 | # Implementation Details
|
2 | 2 |
|
| 3 | +### Abstract Operations on Python Objects |
| 4 | + |
| 5 | +Many generic operations on Python objects in CPython are defined in the header |
| 6 | +files `abstract.c` and `abstract.h`. These operations are widely used and their |
| 7 | +interplay and intricacies are the cause for the conversion, error message, and |
| 8 | +control flow bugs when not mimicked correctly. Our current approach is to |
| 9 | +provide many of these abstract operations as part of the |
| 10 | +`PythonObjectLibrary`. Usually, this means there are at least two messages for |
| 11 | +each operation - one that takes a `ThreadState` argument, and one that |
| 12 | +doesn't. The intent is to allow passing of exception state and caller |
| 13 | +information similar to how we do it with the `PFrame` argument even across |
| 14 | +library messages, which cannot take a VirtualFrame. |
| 15 | + |
| 16 | +All nodes that are used in message implementations must allow uncached |
| 17 | +usage. Often (e.g. in the case of the generic `CallNode`) they offer execute |
| 18 | +methods with and without frames. If a `ThreadState` was passed to the message, a |
| 19 | +frame to pass to the node can be reconstructed using |
| 20 | +`PArguments.frameForCall(threadState)`. Here's an example: |
| 21 | + |
| 22 | +```java |
| 23 | +@ExportMessage |
| 24 | +long messageWithState(ThreadState state, |
| 25 | + @Cached CallNode callNode) { |
| 26 | + Object callable = ... |
| 27 | + |
| 28 | + if (state != null) { |
| 29 | + return callNode.execute(PArguments.frameForCall(state), callable, arguments); |
| 30 | + } else { |
| 31 | + return callNode.execute(callable, arguments); |
| 32 | + } |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +*Note*: It is **always** preferable to call an `execute` method with a |
| 37 | +`VirtualFrame` when both one with and without exist! The reason is that this |
| 38 | +avoids materialization of the frame state in more cases, as described on the |
| 39 | +section on Python's global thread state above. |
| 40 | + |
3 | 41 | ### Python Global Thread State
|
4 | 42 |
|
5 | 43 | In CPython, each stack frame is allocated on the heap, and there's a global
|
@@ -79,41 +117,3 @@ as needed, we use the same mechanism to also pass the currently handled
|
79 | 117 | exception. Unlike CPython we do not use a stack of currently handled exceptions,
|
80 | 118 | instead we utilize the call stack of Java by always passing the current exception
|
81 | 119 | and holding on to the last (if any) in a local variable.
|
82 |
| - |
83 |
| -### Abstract Operations on Python Objects |
84 |
| - |
85 |
| -Many generic operations on Python objects in CPython are defined in the header |
86 |
| -files `abstract.c` and `abstract.h`. These operations are widely used and their |
87 |
| -interplay and intricacies are the cause for the conversion, error message, and |
88 |
| -control flow bugs when not mimicked correctly. Our current approach is to |
89 |
| -provide many of these abstract operations as part of the |
90 |
| -`PythonObjectLibrary`. Usually, this means there are at least two messages for |
91 |
| -each operation - one that takes a `ThreadState` argument, and one that |
92 |
| -doesn't. The intent is to allow passing of exception state and caller |
93 |
| -information similar to how we do it with the `PFrame` argument even across |
94 |
| -library messages, which cannot take a VirtualFrame. |
95 |
| - |
96 |
| -All nodes that are used in message implementations must allow uncached |
97 |
| -usage. Often (e.g. in the case of the generic `CallNode`) they offer execute |
98 |
| -methods with and without frames. If a `ThreadState` was passed to the message, a |
99 |
| -frame to pass to the node can be reconstructed using |
100 |
| -`PArguments.frameForCall(threadState)`. Here's an example: |
101 |
| - |
102 |
| -```java |
103 |
| -@ExportMessage |
104 |
| -long messageWithState(ThreadState state, |
105 |
| - @Cached CallNode callNode) { |
106 |
| - Object callable = ... |
107 |
| - |
108 |
| - if (state != null) { |
109 |
| - return callNode.execute(PArguments.frameForCall(state), callable, arguments); |
110 |
| - } else { |
111 |
| - return callNode.execute(callable, arguments); |
112 |
| - } |
113 |
| -} |
114 |
| -``` |
115 |
| - |
116 |
| -*Note*: It is **always** preferable to call an `execute` method with a |
117 |
| -`VirtualFrame` when both one with and without exist! The reason is that this |
118 |
| -avoids materialization of the frame state in more cases, as described on the |
119 |
| -section on Python's global thread state above. |
0 commit comments