Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Xavier Thompson
cython
Commits
8884f531
Commit
8884f531
authored
Jun 04, 2020
by
Xavier Thompson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Inject wrapper classes into the AST only after the declaration analysis phase
parent
080269ba
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
74 additions
and
62 deletions
+74
-62
Cython/Compiler/CypclassWrapper.py
Cython/Compiler/CypclassWrapper.py
+46
-47
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+8
-0
Cython/Compiler/Pipeline.py
Cython/Compiler/Pipeline.py
+1
-1
Cython/Compiler/PyrexTypes.py
Cython/Compiler/PyrexTypes.py
+19
-12
Cython/Compiler/Symtab.py
Cython/Compiler/Symtab.py
+0
-2
No files found.
Cython/Compiler/CypclassWrapper.py
View file @
8884f531
...
...
@@ -93,56 +93,55 @@ underlying_name = EncodedString("nogil_cyobject")
# - Insert additional cclass wrapper nodes by returning lists of nodes
# => must run after NormalizeTree (otherwise single statements might not be held in a list)
#
class
CypclassWrapperInjection
(
Visitor
Transform
):
class
CypclassWrapperInjection
(
Cython
Transform
):
"""
Synthesize and insert a wrapper c class at the module level for each cypclass that supports it.
- Even nested cypclasses have their wrapper at the module level.
- Must run after NormalizeTree.
- The root node passed when calling this visitor should not be lower than a ModuleNode.
"""
def
__call__
(
self
,
root
):
self
.
cypclass_wrappers_stack
=
[]
self
.
nesting_stack
=
[]
self
.
module_scope
=
root
.
scope
from
.ParseTreeTransforms
import
AnalyseDeclarationsTransform
self
.
analyser
=
AnalyseDeclarationsTransform
(
self
.
context
)
return
super
(
CypclassWrapperInjection
,
self
).
__call__
(
root
)
def
visit_Node
(
self
,
node
):
def
visit_ModuleNode
(
self
,
node
):
self
.
cypclass_wrappers
=
[]
self
.
nesting_stack
=
[]
self
.
module_scope
=
node
.
scope
self
.
visitchildren
(
node
)
self
.
inject_cypclass_wrappers
(
node
)
return
node
def
inject_cypclass_wrappers
(
self
,
module_node
):
fake_module_node
=
module_node
.
clone_node
()
fake_module_node
.
body
=
Nodes
.
StatListNode
(
module_node
.
body
.
pos
,
stats
=
self
.
cypclass_wrappers
)
self
.
analyser
(
fake_module_node
)
module_node
.
body
.
stats
.
extend
(
fake_module_node
.
body
.
stats
)
# TODO: can cypclasses be nested in something other than this ?
# can cypclasses even be nested in non-cypclass cpp classes, or structs ?
def
visit_CStructOrUnionDefNode
(
self
,
node
):
self
.
nesting_stack
.
append
(
node
)
self
.
visitchildren
(
node
)
self
.
nesting_stack
.
pop
()
top_level
=
not
self
.
nesting_stack
if
top_level
:
return_nodes
=
[
node
]
for
wrapper
in
self
.
cypclass_wrappers_stack
:
return_nodes
.
append
(
wrapper
)
self
.
cypclass_wrappers_stack
.
clear
()
return
return_nodes
return
node
def
visit_CppClassNode
(
self
,
node
):
if
node
.
cypclass
:
wrapper
=
self
.
synthesize_wrapper_cclass
(
node
)
if
wrapper
is
not
None
:
self
.
cypclass_wrappers_stack
.
append
(
wrapper
)
# visit children and return all wrappers when at the top level
# forward-declare the wrapper
wrapper
.
declare
(
self
.
module_scope
)
self
.
cypclass_wrappers
.
append
(
wrapper
)
# visit children and keep track of nesting
return
self
.
visit_CStructOrUnionDefNode
(
node
)
def
find_module_scope
(
self
,
scope
):
module_scope
=
scope
while
module_scope
and
not
module_scope
.
is_module_scope
:
module_scope
=
module_scope
.
outer_scope
return
module_scope
def
iter_wrapper_methods
(
self
,
wrapper_cclass
):
for
node
in
wrapper_cclass
.
body
.
stats
:
if
isinstance
(
node
,
Nodes
.
DefNode
):
yield
node
def
synthesize_wrapper_cclass
(
self
,
node
):
if
node
.
templates
:
# Python wrapper for templated cypclasses not supported yet
...
...
@@ -152,39 +151,36 @@ class CypclassWrapperInjection(VisitorTransform):
# whether the is declared with ':' and a suite, or just a forward declaration
node_has_suite
=
node
.
attributes
is
not
None
# TODO: forward declare wrapper classes too ?
if
not
node_has_suite
:
return
None
# TODO: take nesting into account for the name
# TODO: check that there is no collision with another name
cclass_name
=
EncodedString
(
"%s_cyp_wrapper"
%
node
.
name
)
from
.ExprNodes
import
TupleNode
bases_args
=
[]
if
node
.
base_classes
:
first_base
=
node
.
base_classes
[
0
]
if
isinstance
(
first_base
,
Nodes
.
CSimpleBaseTypeNode
)
and
first_base
.
templates
is
None
:
first_base_name
=
first_base
.
name
builtin_entry
=
self
.
module_scope
.
lookup
(
first_base_name
)
if
builtin_entry
is
not
None
:
return
wrapped_first_base
=
Nodes
.
CSimpleBaseTypeNode
(
first_base
.
pos
,
name
=
"%s_cyp_wrapper"
%
first_base_name
,
module_path
=
[],
is_basic_c_type
=
first_base
.
is_basic_c_type
,
signed
=
first_base
.
signed
,
complex
=
first_base
.
complex
,
longness
=
first_base
.
longness
,
is_self_arg
=
first_base
.
is_self_arg
,
templates
=
None
)
bases_args
.
append
(
wrapped_first_base
)
node_type
=
node
.
entry
.
type
node_type
.
find_wrapped_base_type
()
first_wrapped_base
=
node_type
.
first_wrapped_base
if
first_wrapped_base
:
first_base_wrapper_name
=
first_wrapped_base
.
wrapper_type
.
name
wrapped_first_base
=
Nodes
.
CSimpleBaseTypeNode
(
node
.
pos
,
name
=
first_base_wrapper_name
,
module_path
=
[],
is_basic_c_type
=
0
,
signed
=
1
,
complex
=
0
,
longness
=
0
,
is_self_arg
=
0
,
templates
=
None
)
bases_args
.
append
(
wrapped_first_base
)
cclass_bases
=
TupleNode
(
node
.
pos
,
args
=
bases_args
)
# the underlying cyobject must come first thing after PyObject_HEAD in the memory layout
# long term, only the base class will declare the underlying attribute
stats
=
[]
if
not
bases_args
:
underlying_cyobject
=
self
.
synthesize_underlying_cyobject_attribute
(
node
)
...
...
@@ -211,6 +207,9 @@ class CypclassWrapperInjection(VisitorTransform):
wrapped_cypclass
=
node
,
)
# indicate that the cypclass will have a wrapper
node
.
entry
.
type
.
support_wrapper
=
True
return
wrapper
def
synthesize_underlying_cyobject_attribute
(
self
,
node
):
...
...
Cython/Compiler/Nodes.py
View file @
8884f531
...
...
@@ -5352,6 +5352,14 @@ class CypclassWrapperDefNode(CClassDefNode):
is_cyp_wrapper
=
1
def
declare
(
self
,
env
):
# > declare the same way as a standard c class
super
(
CypclassWrapperDefNode
,
self
).
declare
(
env
)
# > mark the wrapper type as such
self
.
entry
.
type
.
is_cyp_wrapper
=
1
# > associate the wrapper type to the wrapped type
self
.
wrapped_cypclass
.
entry
.
type
.
wrapper_type
=
self
.
entry
.
type
def
analyse_declarations
(
self
,
env
):
# > analyse declarations before inserting methods
super
(
CypclassWrapperDefNode
,
self
).
analyse_declarations
(
env
)
...
...
Cython/Compiler/Pipeline.py
View file @
8884f531
...
...
@@ -181,7 +181,6 @@ def create_pipeline(context, mode, exclude_classes=()):
# compilation stage.
stages
=
[
NormalizeTree
(
context
),
CypclassWrapperInjection
(),
PostParse
(
context
),
_specific_post_parse
,
TrackNumpyAttributes
(),
...
...
@@ -198,6 +197,7 @@ def create_pipeline(context, mode, exclude_classes=()):
ForwardDeclareTypes
(
context
),
InjectGilHandling
(),
AnalyseDeclarationsTransform
(
context
),
CypclassWrapperInjection
(
context
),
AutoTestDictTransform
(
context
),
EmbedSignature
(
context
),
ReplacePropertyNode
(
context
),
...
...
Cython/Compiler/PyrexTypes.py
View file @
8884f531
...
...
@@ -3962,32 +3962,39 @@ def compute_mro_generic(cls):
class
CypClassType
(
CppClassType
):
# lock_mode string (tri-state: "nolock"/"checklock"/"autolock")
# _mro [CppClassType] or None the Method Resolution Order of this cypclass according to Python
# support_wrapper boolean whether this cypclass will be wrapped
# wrapper_type PyExtensionType or None the type of the cclass wrapper
# wrapped_base_type CypClassType or None the type of the oldest wrapped cypclass base
# first_wrapped_base CypClassType or None the first cypclass base that has a wrapper if there is one
# wrapped_base_type CypClassType or None the type of the oldest wrapped cypclass base
is_cyp_class
=
1
to_py_function
=
None
def
__init__
(
self
,
name
,
scope
,
cname
,
base_classes
,
templates
=
None
,
template_type
=
None
,
nogil
=
0
,
lock_mode
=
None
,
activable
=
False
):
CppClassType
.
__init__
(
self
,
name
,
scope
,
cname
,
base_classes
,
templates
,
template_type
,
nogil
)
if
base_classes
:
self
.
find_wrapped_base_type
(
base_classes
)
self
.
lock_mode
=
lock_mode
if
lock_mode
else
"autolock"
self
.
activable
=
activable
self
.
_mro
=
None
self
.
support_wrapper
=
False
self
.
wrapper_type
=
None
self
.
wrapped_base_type
=
None
def
find_wrapped_base_type
(
self
,
base_classes
):
first_wrapped_cypclass_base
=
None
for
base_type
in
base_classes
:
if
base_type
.
is_cyp_class
and
base_type
.
wrapper_type
:
first_wrapped_cypclass_base
=
base_type
# find the first base that has a wrapper, if there is one
# find the oldest superclass such that all intervening classes have a wrapper
def
find_wrapped_base_type
(
self
):
# default: the oldest superclass is self and there are no bases
self
.
wrapped_base_type
=
self
self
.
first_wrapped_base
=
None
# if there are no bases, no need to look further
if
not
self
.
base_classes
:
return
# otherwise, find the first wrapped base (if there is one) and take the same oldest superclass
for
base_type
in
self
.
base_classes
:
if
base_type
.
is_cyp_class
and
base_type
.
support_wrapper
:
# this base type is the first wrapped base
self
.
first_wrapped_base
=
base_type
self
.
wrapped_base_type
=
base_type
.
wrapped_base_type
break
if
first_wrapped_cypclass_base
:
self
.
wrapped_base_type
=
first_wrapped_cypclass_base
.
wrapped_base_type
else
:
self
.
wrapped_base_type
=
self
# Return the MRO for this cypclass
# Compute all the mro needed when a previous computation is not available
...
...
Cython/Compiler/Symtab.py
View file @
8884f531
...
...
@@ -735,8 +735,6 @@ class Scope(object):
entry
.
already_declared_here
()
else
:
entry
.
type
.
base_classes
=
base_classes
if
cypclass
:
entry
.
type
.
find_wrapped_base_type
(
base_classes
)
if
templates
or
entry
.
type
.
templates
:
if
templates
!=
entry
.
type
.
templates
:
error
(
pos
,
"Template parameters do not match previous declaration"
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment