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
Boxiang Sun
cython
Commits
b22011f8
Commit
b22011f8
authored
Jan 13, 2012
by
Vitja Makarov
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #80 from vitek/_defnode_refactor
DefNode refactoring and initial closure function call inlining
parents
262bc9fb
d8fa1dba
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
575 additions
and
173 deletions
+575
-173
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+100
-2
Cython/Compiler/FlowControl.py
Cython/Compiler/FlowControl.py
+28
-9
Cython/Compiler/Naming.py
Cython/Compiler/Naming.py
+1
-0
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+277
-152
Cython/Compiler/Optimize.py
Cython/Compiler/Optimize.py
+22
-0
Cython/Compiler/Options.py
Cython/Compiler/Options.py
+3
-0
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+61
-7
Cython/Compiler/Pipeline.py
Cython/Compiler/Pipeline.py
+2
-0
Cython/Compiler/Symtab.py
Cython/Compiler/Symtab.py
+1
-0
tests/run/closure_inlining.pyx
tests/run/closure_inlining.pyx
+77
-0
tests/run/type_inference.pyx
tests/run/type_inference.pyx
+3
-3
No files found.
Cython/Compiler/ExprNodes.py
View file @
b22011f8
...
@@ -3864,6 +3864,103 @@ class SimpleCallNode(CallNode):
...
@@ -3864,6 +3864,103 @@ class SimpleCallNode(CallNode):
code
.
funcstate
.
release_temp
(
self
.
opt_arg_struct
)
code
.
funcstate
.
release_temp
(
self
.
opt_arg_struct
)
class
InlinedDefNodeCallNode
(
CallNode
):
# Inline call to defnode
#
# function PyCFunctionNode
# function_name NameNode
# args [ExprNode]
subexprs
=
[
'args'
,
'function_name'
]
is_temp
=
1
type
=
py_object_type
function
=
None
function_name
=
None
def
can_be_inlined
(
self
):
func_type
=
self
.
function
.
def_node
if
func_type
.
star_arg
or
func_type
.
starstar_arg
:
return
False
if
len
(
func_type
.
args
)
!=
len
(
self
.
args
):
return
False
return
True
def
analyse_types
(
self
,
env
):
self
.
function_name
.
analyse_types
(
env
)
for
arg
in
self
.
args
:
arg
.
analyse_types
(
env
)
func_type
=
self
.
function
.
def_node
actual_nargs
=
len
(
self
.
args
)
# Coerce arguments
some_args_in_temps
=
False
for
i
in
xrange
(
actual_nargs
):
formal_type
=
func_type
.
args
[
i
].
type
arg
=
self
.
args
[
i
].
coerce_to
(
formal_type
,
env
)
if
arg
.
is_temp
:
if
i
>
0
:
# first argument in temp doesn't impact subsequent arguments
some_args_in_temps
=
True
elif
arg
.
type
.
is_pyobject
and
not
env
.
nogil
:
if
arg
.
nonlocally_immutable
():
# plain local variables are ok
pass
else
:
# we do not safely own the argument's reference,
# but we must make sure it cannot be collected
# before we return from the function, so we create
# an owned temp reference to it
if
i
>
0
:
# first argument doesn't matter
some_args_in_temps
=
True
arg
=
arg
.
coerce_to_temp
(
env
)
self
.
args
[
i
]
=
arg
if
some_args_in_temps
:
# if some args are temps and others are not, they may get
# constructed in the wrong order (temps first) => make
# sure they are either all temps or all not temps (except
# for the last argument, which is evaluated last in any
# case)
for
i
in
xrange
(
actual_nargs
-
1
):
arg
=
self
.
args
[
i
]
if
arg
.
nonlocally_immutable
():
# locals, C functions, unassignable types are safe.
pass
elif
arg
.
type
.
is_cpp_class
:
# Assignment has side effects, avoid.
pass
elif
env
.
nogil
and
arg
.
type
.
is_pyobject
:
# can't copy a Python reference into a temp in nogil
# env (this is safe: a construction would fail in
# nogil anyway)
pass
else
:
#self.args[i] = arg.coerce_to_temp(env)
# instead: issue a warning
if
i
>
0
:
warning
(
arg
.
pos
,
"Argument evaluation order in C function call is undefined and may not be as expected"
,
0
)
break
def
generate_result_code
(
self
,
code
):
arg_code
=
[
self
.
function_name
.
py_result
()]
func_type
=
self
.
function
.
def_node
for
arg
,
proto_arg
in
zip
(
self
.
args
,
func_type
.
args
):
if
arg
.
type
.
is_pyobject
:
arg_code
.
append
(
arg
.
result_as
(
proto_arg
.
type
))
else
:
arg_code
.
append
(
arg
.
result
())
arg_code
=
', '
.
join
(
arg_code
)
code
.
putln
(
"%s = %s(%s); %s"
%
(
self
.
result
(),
self
.
function
.
def_node
.
entry
.
pyfunc_cname
,
arg_code
,
code
.
error_goto_if_null
(
self
.
result
(),
self
.
pos
)))
code
.
put_gotref
(
self
.
py_result
())
class
PythonCapiFunctionNode
(
ExprNode
):
class
PythonCapiFunctionNode
(
ExprNode
):
subexprs
=
[]
subexprs
=
[]
def
__init__
(
self
,
pos
,
py_name
,
cname
,
func_type
,
utility_code
=
None
):
def
__init__
(
self
,
pos
,
py_name
,
cname
,
func_type
,
utility_code
=
None
):
...
@@ -6252,15 +6349,16 @@ class GeneratorExpressionNode(LambdaNode):
...
@@ -6252,15 +6349,16 @@ class GeneratorExpressionNode(LambdaNode):
super
(
GeneratorExpressionNode
,
self
).
analyse_declarations
(
env
)
super
(
GeneratorExpressionNode
,
self
).
analyse_declarations
(
env
)
# No pymethdef required
# No pymethdef required
self
.
def_node
.
pymethdef_required
=
False
self
.
def_node
.
pymethdef_required
=
False
self
.
def_node
.
py_wrapper_required
=
False
self
.
def_node
.
is_cyfunction
=
False
self
.
def_node
.
is_cyfunction
=
False
# Force genexpr signature
# Force genexpr signature
self
.
def_node
.
entry
.
signature
=
TypeSlots
.
pyfunction_noargs
self
.
def_node
.
entry
.
signature
=
TypeSlots
.
pyfunction_noargs
def
generate_result_code
(
self
,
code
):
def
generate_result_code
(
self
,
code
):
code
.
putln
(
code
.
putln
(
'%s = %s(%s
, NULL
); %s'
%
(
'%s = %s(%s); %s'
%
(
self
.
result
(),
self
.
result
(),
self
.
def_node
.
entry
.
func_cname
,
self
.
def_node
.
entry
.
py
func_cname
,
self
.
self_result_code
(),
self
.
self_result_code
(),
code
.
error_goto_if_null
(
self
.
result
(),
self
.
pos
)))
code
.
error_goto_if_null
(
self
.
result
(),
self
.
pos
)))
code
.
put_gotref
(
self
.
py_result
())
code
.
put_gotref
(
self
.
py_result
())
...
...
Cython/Compiler/FlowControl.py
View file @
b22011f8
...
@@ -327,6 +327,32 @@ class NameReference(object):
...
@@ -327,6 +327,32 @@ class NameReference(object):
return
'%s(entry=%r)'
%
(
self
.
__class__
.
__name__
,
self
.
entry
)
return
'%s(entry=%r)'
%
(
self
.
__class__
.
__name__
,
self
.
entry
)
class
ControlFlowState
(
list
):
# Keeps track of Node's entry assignments
#
# cf_is_null [boolean] It is uninitialized
# cf_maybe_null [boolean] May be uninitialized
# is_single [boolean] Has only one assignment at this point
cf_maybe_null
=
False
cf_is_null
=
False
is_single
=
False
def
__init__
(
self
,
state
):
if
Uninitialized
in
state
:
state
.
discard
(
Uninitialized
)
self
.
cf_maybe_null
=
True
if
not
state
:
self
.
cf_is_null
=
True
else
:
if
len
(
state
)
==
1
:
self
.
is_single
=
True
super
(
ControlFlowState
,
self
).
__init__
(
state
)
def
one
(
self
):
return
self
[
0
]
class
GVContext
(
object
):
class
GVContext
(
object
):
"""Graphviz subgraph object."""
"""Graphviz subgraph object."""
...
@@ -530,11 +556,10 @@ def check_definitions(flow, compiler_directives):
...
@@ -530,11 +556,10 @@ def check_definitions(flow, compiler_directives):
messages
.
report
()
messages
.
report
()
# Remove Uninitialized from cf_state
for
node
in
assmt_nodes
:
for
node
in
assmt_nodes
:
node
.
cf_state
.
discard
(
Uninitialized
)
node
.
cf_state
=
ControlFlowState
(
node
.
cf_state
)
for
node
in
references
:
for
node
in
references
:
node
.
cf_state
.
discard
(
Uninitialized
)
node
.
cf_state
=
ControlFlowState
(
node
.
cf_state
)
class
AssignmentCollector
(
TreeVisitor
):
class
AssignmentCollector
(
TreeVisitor
):
...
@@ -632,13 +657,7 @@ class ControlFlowAnalysis(CythonTransform):
...
@@ -632,13 +657,7 @@ class ControlFlowAnalysis(CythonTransform):
return
node
return
node
def
visit_DefNode
(
self
,
node
):
def
visit_DefNode
(
self
,
node
):
## XXX: no target name node here
node
.
used
=
True
node
.
used
=
True
entry
=
node
.
entry
if
entry
.
is_anonymous
:
entry
=
self
.
env
.
lookup
(
node
.
name
)
if
entry
:
self
.
flow
.
mark_assignment
(
node
,
object_expr_not_none
,
entry
)
return
self
.
visit_FuncDefNode
(
node
)
return
self
.
visit_FuncDefNode
(
node
)
def
visit_GeneratorBodyDefNode
(
self
,
node
):
def
visit_GeneratorBodyDefNode
(
self
,
node
):
...
...
Cython/Compiler/Naming.py
View file @
b22011f8
...
@@ -19,6 +19,7 @@ funcdoc_prefix = pyrex_prefix + "doc_"
...
@@ -19,6 +19,7 @@ funcdoc_prefix = pyrex_prefix + "doc_"
enum_prefix
=
pyrex_prefix
+
"e_"
enum_prefix
=
pyrex_prefix
+
"e_"
func_prefix
=
pyrex_prefix
+
"f_"
func_prefix
=
pyrex_prefix
+
"f_"
pyfunc_prefix
=
pyrex_prefix
+
"pf_"
pyfunc_prefix
=
pyrex_prefix
+
"pf_"
pywrap_prefix
=
pyrex_prefix
+
"pw_"
genbody_prefix
=
pyrex_prefix
+
"gb_"
genbody_prefix
=
pyrex_prefix
+
"gb_"
gstab_prefix
=
pyrex_prefix
+
"getsets_"
gstab_prefix
=
pyrex_prefix
+
"getsets_"
prop_get_prefix
=
pyrex_prefix
+
"getprop_"
prop_get_prefix
=
pyrex_prefix
+
"getprop_"
...
...
Cython/Compiler/Nodes.py
View file @
b22011f8
...
@@ -1308,7 +1308,6 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1308,7 +1308,6 @@ class FuncDefNode(StatNode, BlockNode):
# with fused argument types with a FusedCFuncDefNode
# with fused argument types with a FusedCFuncDefNode
py_func
=
None
py_func
=
None
assmt
=
None
needs_closure
=
False
needs_closure
=
False
needs_outer_scope
=
False
needs_outer_scope
=
False
pymethdef_required
=
False
pymethdef_required
=
False
...
@@ -1405,14 +1404,7 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1405,14 +1404,7 @@ class FuncDefNode(StatNode, BlockNode):
if
'cython_unused'
not
in
self
.
modifiers
:
if
'cython_unused'
not
in
self
.
modifiers
:
self
.
modifiers
=
self
.
modifiers
+
[
'cython_unused'
]
self
.
modifiers
=
self
.
modifiers
+
[
'cython_unused'
]
preprocessor_guard
=
None
preprocessor_guard
=
self
.
get_preprocessor_guard
()
if
self
.
entry
.
is_special
and
not
is_buffer_slot
:
slot
=
TypeSlots
.
method_name_to_slot
.
get
(
self
.
entry
.
name
)
if
slot
:
preprocessor_guard
=
slot
.
preprocessor_guard_code
()
if
(
self
.
entry
.
name
==
'__long__'
and
not
self
.
entry
.
scope
.
lookup_here
(
'__int__'
)):
preprocessor_guard
=
None
profile
=
code
.
globalstate
.
directives
[
'profile'
]
profile
=
code
.
globalstate
.
directives
[
'profile'
]
if
profile
and
lenv
.
nogil
:
if
profile
and
lenv
.
nogil
:
...
@@ -1459,7 +1451,7 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1459,7 +1451,7 @@ class FuncDefNode(StatNode, BlockNode):
self
.
generate_argument_declarations
(
lenv
,
code
)
self
.
generate_argument_declarations
(
lenv
,
code
)
for
entry
in
lenv
.
var_entries
:
for
entry
in
lenv
.
var_entries
:
if
not
entry
.
in_closure
:
if
not
(
entry
.
in_closure
or
entry
.
is_arg
)
:
code
.
put_var_declaration
(
entry
)
code
.
put_var_declaration
(
entry
)
# Initialize the return variable __pyx_r
# Initialize the return variable __pyx_r
...
@@ -1555,7 +1547,8 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1555,7 +1547,8 @@ class FuncDefNode(StatNode, BlockNode):
is_cdef
=
isinstance
(
self
,
CFuncDefNode
)
is_cdef
=
isinstance
(
self
,
CFuncDefNode
)
for
entry
in
lenv
.
arg_entries
:
for
entry
in
lenv
.
arg_entries
:
if
entry
.
type
.
is_pyobject
:
if
entry
.
type
.
is_pyobject
:
if
(
acquire_gil
or
entry
.
assignments
)
and
not
entry
.
in_closure
:
if
((
acquire_gil
or
len
(
entry
.
cf_assignments
)
>
1
)
and
not
entry
.
in_closure
):
code
.
put_var_incref
(
entry
)
code
.
put_var_incref
(
entry
)
# Note: defaults are always increffed. For def functions, we
# Note: defaults are always increffed. For def functions, we
...
@@ -1565,6 +1558,9 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1565,6 +1558,9 @@ class FuncDefNode(StatNode, BlockNode):
if
is_cdef
and
entry
.
type
.
is_memoryviewslice
:
if
is_cdef
and
entry
.
type
.
is_memoryviewslice
:
code
.
put_incref_memoryviewslice
(
entry
.
cname
,
code
.
put_incref_memoryviewslice
(
entry
.
cname
,
have_gil
=
not
lenv
.
nogil
)
have_gil
=
not
lenv
.
nogil
)
for
entry
in
lenv
.
var_entries
:
if
entry
.
is_arg
and
len
(
entry
.
cf_assignments
)
>
1
:
code
.
put_var_incref
(
entry
)
# ----- Initialise local buffer auxiliary variables
# ----- Initialise local buffer auxiliary variables
for
entry
in
lenv
.
var_entries
+
lenv
.
arg_entries
:
for
entry
in
lenv
.
var_entries
+
lenv
.
arg_entries
:
...
@@ -1709,14 +1705,15 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1709,14 +1705,15 @@ class FuncDefNode(StatNode, BlockNode):
if
entry
.
type
.
is_memoryviewslice
:
if
entry
.
type
.
is_memoryviewslice
:
code
.
put_xdecref_memoryviewslice
(
entry
.
cname
,
code
.
put_xdecref_memoryviewslice
(
entry
.
cname
,
have_gil
=
not
lenv
.
nogil
)
have_gil
=
not
lenv
.
nogil
)
elif
entry
.
type
.
is_pyobject
:
elif
entry
.
type
.
is_pyobject
:
code
.
put_var_decref
(
entry
)
if
not
entry
.
is_arg
or
len
(
entry
.
cf_assignments
)
>
1
:
code
.
put_var_decref
(
entry
)
# Decref any increfed args
# Decref any increfed args
for
entry
in
lenv
.
arg_entries
:
for
entry
in
lenv
.
arg_entries
:
if
entry
.
type
.
is_pyobject
:
if
entry
.
type
.
is_pyobject
:
if
(
acquire_gil
or
entry
.
assignments
)
and
not
entry
.
in_closure
:
if
((
acquire_gil
or
len
(
entry
.
cf_assignments
)
>
1
)
and
not
entry
.
in_closure
):
code
.
put_var_decref
(
entry
)
code
.
put_var_decref
(
entry
)
if
entry
.
type
.
is_memoryviewslice
:
if
entry
.
type
.
is_memoryviewslice
:
code
.
put_xdecref_memoryviewslice
(
entry
.
cname
,
code
.
put_xdecref_memoryviewslice
(
entry
.
cname
,
...
@@ -1820,9 +1817,6 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1820,9 +1817,6 @@ class FuncDefNode(StatNode, BlockNode):
for
arg
in
self
.
args
:
for
arg
in
self
.
args
:
if
not
arg
.
is_dynamic
:
if
not
arg
.
is_dynamic
:
arg
.
generate_assignment_code
(
code
)
arg
.
generate_assignment_code
(
code
)
# For Python class methods, create and store function object
if
self
.
assmt
:
self
.
assmt
.
generate_execution_code
(
code
)
#
#
# Special code for the __getbuffer__ function
# Special code for the __getbuffer__ function
...
@@ -1853,6 +1847,21 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1853,6 +1847,21 @@ class FuncDefNode(StatNode, BlockNode):
code
.
putln
(
"__Pyx_DECREF(Py_None); %s->obj = NULL;"
%
info
)
code
.
putln
(
"__Pyx_DECREF(Py_None); %s->obj = NULL;"
%
info
)
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
def
get_preprocessor_guard
(
self
):
is_buffer_slot
=
((
self
.
entry
.
name
==
"__getbuffer__"
and
self
.
entry
.
scope
.
is_c_class_scope
)
or
(
self
.
entry
.
name
==
"__releasebuffer__"
and
self
.
entry
.
scope
.
is_c_class_scope
))
if
self
.
entry
.
is_special
and
not
is_buffer_slot
:
slot
=
TypeSlots
.
method_name_to_slot
.
get
(
self
.
entry
.
name
)
if
slot
:
preprocessor_guard
=
slot
.
preprocessor_guard_code
()
if
(
self
.
entry
.
name
==
'__long__'
and
not
self
.
entry
.
scope
.
lookup_here
(
'__int__'
)):
preprocessor_guard
=
None
return
preprocessor_guard
class
CFuncDefNode
(
FuncDefNode
):
class
CFuncDefNode
(
FuncDefNode
):
# C function definition.
# C function definition.
#
#
...
@@ -2548,7 +2557,9 @@ def __pyx_fused_cpdef(signatures, args, kwargs):
...
@@ -2548,7 +2557,9 @@ def __pyx_fused_cpdef(signatures, args, kwargs):
py_func
.
analyse_declarations
(
env
)
py_func
.
analyse_declarations
(
env
)
# ... and its body
# ... and its body
py_func
.
scope
=
env
py_func
.
scope
=
env
ParseTreeTransforms
.
AnalyseDeclarationsTransform
(
None
)(
py_func
)
# Will be analysed later by underlying AnalyseDeclarationsTransform
#ParseTreeTransforms.AnalyseDeclarationsTransform(None)(py_func)
e
,
orig_e
=
py_func
.
entry
,
orig_py_func
.
entry
e
,
orig_e
=
py_func
.
entry
,
orig_py_func
.
entry
...
@@ -2607,6 +2618,8 @@ class PyArgDeclNode(Node):
...
@@ -2607,6 +2618,8 @@ class PyArgDeclNode(Node):
# entry Symtab.Entry
# entry Symtab.Entry
# annotation ExprNode or None Py3 argument annotation
# annotation ExprNode or None Py3 argument annotation
child_attrs
=
[]
child_attrs
=
[]
is_self_arg
=
False
is_type_arg
=
False
def
generate_function_definitions
(
self
,
env
,
code
):
def
generate_function_definitions
(
self
,
env
,
code
):
self
.
entry
.
generate_function_definitions
(
env
,
code
)
self
.
entry
.
generate_function_definitions
(
env
,
code
)
...
@@ -2633,8 +2646,6 @@ class DefNode(FuncDefNode):
...
@@ -2633,8 +2646,6 @@ class DefNode(FuncDefNode):
# The following subnode is constructed internally
# The following subnode is constructed internally
# when the def statement is inside a Python class definition.
# when the def statement is inside a Python class definition.
#
#
# assmt AssignmentNode Function construction/assignment
#
# fused_py_func DefNode The original fused cpdef DefNode
# fused_py_func DefNode The original fused cpdef DefNode
# (in case this is a specialization)
# (in case this is a specialization)
# specialized_cpdefs [DefNode] list of specialized cpdef DefNodes
# specialized_cpdefs [DefNode] list of specialized cpdef DefNodes
...
@@ -2645,9 +2656,6 @@ class DefNode(FuncDefNode):
...
@@ -2645,9 +2656,6 @@ class DefNode(FuncDefNode):
child_attrs
=
[
"args"
,
"star_arg"
,
"starstar_arg"
,
"body"
,
"decorators"
]
child_attrs
=
[
"args"
,
"star_arg"
,
"starstar_arg"
,
"body"
,
"decorators"
]
lambda_name
=
None
lambda_name
=
None
assmt
=
None
num_kwonly_args
=
0
num_required_kw_args
=
0
reqd_kw_flags_cname
=
"0"
reqd_kw_flags_cname
=
"0"
is_wrapper
=
0
is_wrapper
=
0
no_assignment_synthesis
=
0
no_assignment_synthesis
=
0
...
@@ -2663,6 +2671,9 @@ class DefNode(FuncDefNode):
...
@@ -2663,6 +2671,9 @@ class DefNode(FuncDefNode):
fused_py_func
=
False
fused_py_func
=
False
specialized_cpdefs
=
None
specialized_cpdefs
=
None
py_wrapper
=
None
py_wrapper_required
=
True
func_cname
=
None
def
__init__
(
self
,
pos
,
**
kwds
):
def
__init__
(
self
,
pos
,
**
kwds
):
FuncDefNode
.
__init__
(
self
,
pos
,
**
kwds
)
FuncDefNode
.
__init__
(
self
,
pos
,
**
kwds
)
...
@@ -2781,6 +2792,16 @@ class DefNode(FuncDefNode):
...
@@ -2781,6 +2792,16 @@ class DefNode(FuncDefNode):
self
.
return_type
=
self
.
entry
.
signature
.
return_type
()
self
.
return_type
=
self
.
entry
.
signature
.
return_type
()
self
.
create_local_scope
(
env
)
self
.
create_local_scope
(
env
)
self
.
py_wrapper
=
DefNodeWrapper
(
self
.
pos
,
target
=
self
,
name
=
self
.
entry
.
name
,
args
=
self
.
args
,
star_arg
=
self
.
star_arg
,
starstar_arg
=
self
.
starstar_arg
,
return_type
=
self
.
return_type
)
self
.
py_wrapper
.
analyse_declarations
(
env
)
def
analyse_argument_types
(
self
,
env
):
def
analyse_argument_types
(
self
,
env
):
directive_locals
=
self
.
directive_locals
=
env
.
directives
[
'locals'
]
directive_locals
=
self
.
directive_locals
=
env
.
directives
[
'locals'
]
allow_none_for_extension_args
=
env
.
directives
[
'allow_none_for_extension_args'
]
allow_none_for_extension_args
=
env
.
directives
[
'allow_none_for_extension_args'
]
...
@@ -2926,17 +2947,6 @@ class DefNode(FuncDefNode):
...
@@ -2926,17 +2947,6 @@ class DefNode(FuncDefNode):
"(%d declared, %s expected)"
%
(
"(%d declared, %s expected)"
%
(
desc
,
self
.
name
,
len
(
self
.
args
),
expected_str
))
desc
,
self
.
name
,
len
(
self
.
args
),
expected_str
))
def
signature_has_nongeneric_args
(
self
):
argcount
=
len
(
self
.
args
)
if
argcount
==
0
or
(
argcount
==
1
and
(
self
.
args
[
0
].
is_self_arg
or
self
.
args
[
0
].
is_type_arg
)):
return
0
return
1
def
signature_has_generic_args
(
self
):
return
self
.
entry
.
signature
.
has_generic_args
def
declare_pyfunction
(
self
,
env
):
def
declare_pyfunction
(
self
,
env
):
#print "DefNode.declare_pyfunction:", self.name, "in", env ###
#print "DefNode.declare_pyfunction:", self.name, "in", env ###
name
=
self
.
name
name
=
self
.
name
...
@@ -2950,8 +2960,7 @@ class DefNode(FuncDefNode):
...
@@ -2950,8 +2960,7 @@ class DefNode(FuncDefNode):
entry
=
env
.
declare_pyfunction
(
name
,
self
.
pos
,
allow_redefine
=
not
self
.
is_wrapper
)
entry
=
env
.
declare_pyfunction
(
name
,
self
.
pos
,
allow_redefine
=
not
self
.
is_wrapper
)
self
.
entry
=
entry
self
.
entry
=
entry
prefix
=
env
.
next_id
(
env
.
scope_prefix
)
prefix
=
env
.
next_id
(
env
.
scope_prefix
)
entry
.
func_cname
=
Naming
.
pyfunc_prefix
+
prefix
+
name
self
.
entry
.
pyfunc_cname
=
Naming
.
pyfunc_prefix
+
prefix
+
name
entry
.
pymethdef_cname
=
Naming
.
pymethdef_prefix
+
prefix
+
name
if
Options
.
docstrings
:
if
Options
.
docstrings
:
entry
.
doc
=
embed_position
(
self
.
pos
,
self
.
doc
)
entry
.
doc
=
embed_position
(
self
.
pos
,
self
.
doc
)
entry
.
doc_cname
=
Naming
.
funcdoc_prefix
+
prefix
+
name
entry
.
doc_cname
=
Naming
.
funcdoc_prefix
+
prefix
+
name
...
@@ -2967,6 +2976,7 @@ class DefNode(FuncDefNode):
...
@@ -2967,6 +2976,7 @@ class DefNode(FuncDefNode):
entry
=
env
.
declare_lambda_function
(
self
.
lambda_name
,
self
.
pos
)
entry
=
env
.
declare_lambda_function
(
self
.
lambda_name
,
self
.
pos
)
entry
.
doc
=
None
entry
.
doc
=
None
self
.
entry
=
entry
self
.
entry
=
entry
self
.
entry
.
pyfunc_cname
=
entry
.
cname
def
declare_arguments
(
self
,
env
):
def
declare_arguments
(
self
,
env
):
for
arg
in
self
.
args
:
for
arg
in
self
.
args
:
...
@@ -2981,10 +2991,6 @@ class DefNode(FuncDefNode):
...
@@ -2981,10 +2991,6 @@ class DefNode(FuncDefNode):
arg
.
entry
.
is_arg
=
1
arg
.
entry
.
is_arg
=
1
arg
.
entry
.
used
=
1
arg
.
entry
.
used
=
1
arg
.
entry
.
is_self_arg
=
arg
.
is_self_arg
arg
.
entry
.
is_self_arg
=
arg
.
is_self_arg
if
arg
.
hdr_type
:
if
arg
.
is_self_arg
or
arg
.
is_type_arg
or
\
(
arg
.
type
.
is_extension_type
and
not
arg
.
hdr_type
.
is_extension_type
):
arg
.
entry
.
is_declared_generic
=
1
self
.
declare_python_arg
(
env
,
self
.
star_arg
)
self
.
declare_python_arg
(
env
,
self
.
star_arg
)
self
.
declare_python_arg
(
env
,
self
.
starstar_arg
)
self
.
declare_python_arg
(
env
,
self
.
starstar_arg
)
...
@@ -3005,14 +3011,13 @@ class DefNode(FuncDefNode):
...
@@ -3005,14 +3011,13 @@ class DefNode(FuncDefNode):
self
.
local_scope
.
directives
=
env
.
directives
self
.
local_scope
.
directives
=
env
.
directives
self
.
analyse_default_values
(
env
)
self
.
analyse_default_values
(
env
)
if
self
.
needs_assignment_synthesis
(
env
):
if
not
self
.
needs_assignment_synthesis
(
env
)
and
self
.
decorators
:
# Shouldn't we be doing this at the module level too?
self
.
synthesize_assignment_node
(
env
)
elif
self
.
decorators
:
for
decorator
in
self
.
decorators
[::
-
1
]:
for
decorator
in
self
.
decorators
[::
-
1
]:
decorator
.
decorator
.
analyse_expressions
(
env
)
decorator
.
decorator
.
analyse_expressions
(
env
)
def
needs_assignment_synthesis
(
self
,
env
,
code
=
None
):
def
needs_assignment_synthesis
(
self
,
env
,
code
=
None
):
if
self
.
is_wrapper
:
return
False
if
self
.
specialized_cpdefs
or
self
.
is_staticmethod
:
if
self
.
specialized_cpdefs
or
self
.
is_staticmethod
:
return
True
return
True
if
self
.
no_assignment_synthesis
:
if
self
.
no_assignment_synthesis
:
...
@@ -3027,122 +3032,272 @@ class DefNode(FuncDefNode):
...
@@ -3027,122 +3032,272 @@ class DefNode(FuncDefNode):
return
code
.
globalstate
.
directives
[
'binding'
]
return
code
.
globalstate
.
directives
[
'binding'
]
return
env
.
is_py_class_scope
or
env
.
is_closure_scope
return
env
.
is_py_class_scope
or
env
.
is_closure_scope
def
synthesize_assignment_node
(
self
,
env
):
def
error_value
(
self
):
import
ExprNodes
return
self
.
entry
.
signature
.
error_value
def
caller_will_check_exceptions
(
self
):
return
1
def
generate_function_definitions
(
self
,
env
,
code
):
# Before closure cnames are mangled
if
self
.
py_wrapper_required
:
# func_cname might be modified by @cname
self
.
py_wrapper
.
func_cname
=
self
.
entry
.
func_cname
self
.
py_wrapper
.
generate_function_definitions
(
env
,
code
)
FuncDefNode
.
generate_function_definitions
(
self
,
env
,
code
)
if
self
.
fused_py_func
:
def
generate_function_header
(
self
,
code
,
with_pymethdef
,
proto_only
=
0
):
if
proto_only
:
if
self
.
py_wrapper_required
:
self
.
py_wrapper
.
generate_function_header
(
code
,
with_pymethdef
,
True
)
return
return
arg_code_list
=
[]
if
self
.
entry
.
signature
.
has_dummy_arg
:
if
self
.
needs_outer_scope
or
self
.
defaults_struct
:
self_arg
=
'PyObject *%s'
%
Naming
.
self_cname
else
:
self_arg
=
'CYTHON_UNUSED PyObject *%s'
%
Naming
.
self_cname
arg_code_list
.
append
(
self_arg
)
genv
=
env
def
arg_decl_code
(
arg
):
while
genv
.
is_py_class_scope
or
genv
.
is_c_class_scope
:
entry
=
arg
.
entry
genv
=
genv
.
outer_scope
if
entry
.
in_closure
:
cname
=
entry
.
original_cname
else
:
cname
=
entry
.
cname
decl
=
entry
.
type
.
declaration_code
(
cname
)
if
entry
.
cf_used
:
return
decl
return
'CYTHON_UNUSED '
+
decl
for
arg
in
self
.
args
:
arg_code_list
.
append
(
arg_decl_code
(
arg
))
if
self
.
star_arg
:
arg_code_list
.
append
(
arg_decl_code
(
self
.
star_arg
))
if
self
.
starstar_arg
:
arg_code_list
.
append
(
arg_decl_code
(
self
.
starstar_arg
))
arg_code
=
', '
.
join
(
arg_code_list
)
dc
=
self
.
return_type
.
declaration_code
(
self
.
entry
.
pyfunc_cname
)
decls_code
=
code
.
globalstate
[
'decls'
]
preprocessor_guard
=
self
.
get_preprocessor_guard
()
if
preprocessor_guard
:
decls_code
.
putln
(
preprocessor_guard
)
decls_code
.
putln
(
"static %s(%s); /* proto */"
%
(
dc
,
arg_code
))
if
preprocessor_guard
:
decls_code
.
putln
(
"#endif"
)
code
.
putln
(
"static %s(%s) {"
%
(
dc
,
arg_code
))
def
generate_argument_declarations
(
self
,
env
,
code
):
pass
def
generate_keyword_list
(
self
,
code
):
pass
def
generate_argument_parsing_code
(
self
,
env
,
code
):
# Move arguments into closure if required
def
put_into_closure
(
entry
):
if
entry
.
in_closure
:
code
.
putln
(
'%s = %s;'
%
(
entry
.
cname
,
entry
.
original_cname
))
code
.
put_var_incref
(
entry
)
code
.
put_var_giveref
(
entry
)
for
arg
in
self
.
args
:
put_into_closure
(
arg
.
entry
)
for
arg
in
self
.
star_arg
,
self
.
starstar_arg
:
if
arg
:
put_into_closure
(
arg
.
entry
)
def
generate_argument_type_tests
(
self
,
code
):
pass
class
DefNodeWrapper
(
FuncDefNode
):
# DefNode python wrapper code generator
defnode
=
None
target
=
None
# Target DefNode
def
__init__
(
self
,
*
args
,
**
kwargs
):
FuncDefNode
.
__init__
(
self
,
*
args
,
**
kwargs
)
self
.
num_kwonly_args
=
self
.
target
.
num_kwonly_args
self
.
num_required_kw_args
=
self
.
target
.
num_required_kw_args
self
.
num_required_args
=
self
.
target
.
num_required_args
self
.
self_in_stararg
=
self
.
target
.
self_in_stararg
self
.
signature
=
None
def
analyse_declarations
(
self
,
env
):
target_entry
=
self
.
target
.
entry
name
=
self
.
name
prefix
=
env
.
next_id
(
env
.
scope_prefix
)
target_entry
.
func_cname
=
Naming
.
pywrap_prefix
+
prefix
+
name
target_entry
.
pymethdef_cname
=
Naming
.
pymethdef_prefix
+
prefix
+
name
if
genv
.
is_closure_scope
:
self
.
signature
=
target_entry
.
signature
rhs
=
self
.
py_cfunc_node
=
ExprNodes
.
InnerFunctionNode
(
self
.
pos
,
def_node
=
self
,
def
signature_has_nongeneric_args
(
self
):
pymethdef_cname
=
self
.
entry
.
pymethdef_cname
,
argcount
=
len
(
self
.
args
)
code_object
=
ExprNodes
.
CodeObjectNode
(
self
))
if
argcount
==
0
or
(
argcount
==
1
and
(
self
.
args
[
0
].
is_self_arg
or
self
.
args
[
0
].
is_type_arg
)):
return
0
return
1
def
signature_has_generic_args
(
self
):
return
self
.
signature
.
has_generic_args
def
generate_function_body
(
self
,
code
):
args
=
[]
if
self
.
signature
.
has_dummy_arg
:
args
.
append
(
Naming
.
self_cname
)
for
arg
in
self
.
args
:
if
arg
.
hdr_type
:
args
.
append
(
arg
.
type
.
cast_code
(
arg
.
entry
.
cname
))
else
:
args
.
append
(
arg
.
entry
.
cname
)
if
self
.
star_arg
:
args
.
append
(
self
.
star_arg
.
entry
.
cname
)
if
self
.
starstar_arg
:
args
.
append
(
self
.
starstar_arg
.
entry
.
cname
)
args
=
', '
.
join
(
args
)
if
not
self
.
return_type
.
is_void
:
code
.
put
(
'%s = '
%
Naming
.
retval_cname
)
code
.
putln
(
'%s(%s);'
%
(
self
.
target
.
entry
.
pyfunc_cname
,
args
))
def
generate_function_definitions
(
self
,
env
,
code
):
lenv
=
self
.
target
.
local_scope
# Generate C code for header and body of function
code
.
putln
(
""
)
code
.
putln
(
"/* Python wrapper */"
)
preprocessor_guard
=
self
.
target
.
get_preprocessor_guard
()
if
preprocessor_guard
:
code
.
putln
(
preprocessor_guard
)
code
.
enter_cfunc_scope
()
code
.
return_from_error_cleanup_label
=
code
.
new_label
()
with_pymethdef
=
(
self
.
target
.
needs_assignment_synthesis
(
lenv
,
code
)
or
self
.
target
.
pymethdef_required
)
self
.
generate_function_header
(
code
,
with_pymethdef
)
self
.
generate_argument_declarations
(
lenv
,
code
)
self
.
generate_keyword_list
(
code
)
tempvardecl_code
=
code
.
insertion_point
()
if
self
.
return_type
.
is_pyobject
:
retval_init
=
' = 0'
else
:
else
:
r
hs
=
ExprNodes
.
PyCFunctionNode
(
r
etval_init
=
''
self
.
pos
,
if
not
self
.
return_type
.
is_void
:
def_node
=
self
,
code
.
putln
(
'%s%s;'
%
(
pymethdef_cname
=
self
.
entry
.
pymethdef_cname
,
self
.
return_type
.
declaration_code
(
Naming
.
retval_cname
)
,
binding
=
env
.
directives
[
'binding'
],
retval_init
))
specialized_cpdefs
=
self
.
specialized_cpdefs
,
code
.
put_declare_refcount_context
()
code_object
=
ExprNodes
.
CodeObjectNode
(
self
)
)
code
.
put_setup_refcount_context
(
'%s (wrapper)'
%
self
.
name
)
if
env
.
is_py_class_scope
:
self
.
generate_argument_parsing_code
(
lenv
,
code
)
rhs
.
binding
=
True
self
.
generate_argument_type_tests
(
code
)
self
.
generate_function_body
(
code
)
self
.
is_cyfunction
=
rhs
.
binding
# ----- Go back and insert temp variable declarations
tempvardecl_code
.
put_temp_declarations
(
code
.
funcstate
)
if
self
.
decorators
:
# ----- Error cleanup
for
decorator
in
self
.
decorators
[::
-
1
]
:
if
code
.
error_label
in
code
.
labels_used
:
rhs
=
ExprNodes
.
SimpleCallNode
(
code
.
put_goto
(
code
.
return_label
)
decorator
.
pos
,
code
.
put_label
(
code
.
error_label
)
function
=
decorator
.
decorator
,
for
cname
,
type
in
code
.
funcstate
.
all_managed_temps
():
args
=
[
rhs
]
)
code
.
put_xdecref
(
cname
,
type
)
self
.
assmt
=
SingleAssignmentNode
(
self
.
pos
,
# ----- Non-error return cleanup
lhs
=
ExprNodes
.
NameNode
(
self
.
pos
,
name
=
self
.
name
),
code
.
put_label
(
code
.
return_label
)
rhs
=
rhs
)
for
entry
in
lenv
.
var_entries
:
self
.
assmt
.
analyse_declarations
(
env
)
if
entry
.
is_arg
and
entry
.
type
.
is_pyobject
:
self
.
assmt
.
analyse_expressions
(
env
)
code
.
put_var_decref
(
entry
)
code
.
put_finish_refcount_context
()
if
not
self
.
return_type
.
is_void
:
code
.
putln
(
"return %s;"
%
Naming
.
retval_cname
)
code
.
putln
(
'}'
)
code
.
exit_cfunc_scope
()
if
preprocessor_guard
:
code
.
putln
(
"#endif /*!(%s)*/"
%
preprocessor_guard
)
def
generate_function_header
(
self
,
code
,
with_pymethdef
,
proto_only
=
0
):
def
generate_function_header
(
self
,
code
,
with_pymethdef
,
proto_only
=
0
):
arg_code_list
=
[]
arg_code_list
=
[]
sig
=
self
.
entry
.
signature
sig
=
self
.
signature
if
sig
.
has_dummy_arg
or
self
.
self_in_stararg
:
if
sig
.
has_dummy_arg
or
self
.
self_in_stararg
:
arg_code_list
.
append
(
arg_code_list
.
append
(
"PyObject *%s"
%
Naming
.
self_cname
)
"PyObject *%s"
%
Naming
.
self_cname
)
for
arg
in
self
.
args
:
for
arg
in
self
.
args
:
if
not
arg
.
is_generic
:
if
not
arg
.
is_generic
:
if
arg
.
is_self_arg
or
arg
.
is_type_arg
:
if
arg
.
is_self_arg
or
arg
.
is_type_arg
:
arg_code_list
.
append
(
"PyObject *%s"
%
arg
.
hdr_cname
)
arg_code_list
.
append
(
"PyObject *%s"
%
arg
.
hdr_cname
)
else
:
else
:
decl
=
arg
.
hdr_type
.
declaration_code
(
arg
.
hdr_cname
)
arg_code_list
.
append
(
entry
=
self
.
local_scope
.
lookup
(
arg
.
name
)
arg
.
hdr_type
.
declaration_code
(
arg
.
hdr_cname
))
if
not
entry
.
cf_used
:
entry
=
self
.
target
.
entry
arg_code_list
.
append
(
'CYTHON_UNUSED '
+
decl
)
if
not
entry
.
is_special
and
sig
.
method_flags
()
==
[
TypeSlots
.
method_noargs
]:
else
:
arg_code_list
.
append
(
decl
)
if
not
self
.
entry
.
is_special
and
sig
.
method_flags
()
==
[
TypeSlots
.
method_noargs
]:
arg_code_list
.
append
(
"CYTHON_UNUSED PyObject *unused"
)
arg_code_list
.
append
(
"CYTHON_UNUSED PyObject *unused"
)
if
(
self
.
entry
.
scope
.
is_c_class_scope
and
self
.
entry
.
name
==
"__ipow__"
)
:
if
entry
.
scope
.
is_c_class_scope
and
entry
.
name
==
"__ipow__"
:
arg_code_list
.
append
(
"CYTHON_UNUSED PyObject *unused"
)
arg_code_list
.
append
(
"CYTHON_UNUSED PyObject *unused"
)
if
sig
.
has_generic_args
:
if
sig
.
has_generic_args
:
arg_code_list
.
append
(
arg_code_list
.
append
(
"PyObject *%s, PyObject *%s"
"PyObject *%s, PyObject *%s"
%
(
Naming
.
args_cname
,
Naming
.
kwds_cname
))
%
(
Naming
.
args_cname
,
Naming
.
kwds_cname
))
arg_code
=
", "
.
join
(
arg_code_list
)
arg_code
=
", "
.
join
(
arg_code_list
)
dc
=
self
.
return_type
.
declaration_code
(
self
.
entry
.
func_cname
)
dc
=
self
.
return_type
.
declaration_code
(
entry
.
func_cname
)
mf
=
" "
.
join
(
self
.
modifiers
).
upper
()
header
=
"static %s(%s)"
%
(
dc
,
arg_code
)
if
mf
:
mf
+=
" "
header
=
"static %s%s(%s)"
%
(
mf
,
dc
,
arg_code
)
code
.
putln
(
"%s; /*proto*/"
%
header
)
code
.
putln
(
"%s; /*proto*/"
%
header
)
if
proto_only
:
if
proto_only
:
if
self
.
fused_py_func
:
if
self
.
target
.
fused_py_func
:
# If we are the specialized version of the cpdef, we still
# If we are the specialized version of the cpdef, we still
# want the prototype for the "fused cpdef", in case we're
# want the prototype for the "fused cpdef", in case we're
# checking to see if our method was overridden in Python
# checking to see if our method was overridden in Python
self
.
fused_py_func
.
generate_function_header
(
self
.
target
.
fused_py_func
.
generate_function_header
(
code
,
with_pymethdef
,
proto_only
=
True
)
code
,
with_pymethdef
,
proto_only
=
True
)
return
return
if
(
Options
.
docstrings
and
self
.
entry
.
doc
and
if
(
Options
.
docstrings
and
entry
.
doc
and
not
self
.
fused_py_func
and
not
self
.
target
.
fused_py_func
and
not
self
.
entry
.
scope
.
is_property_scope
and
not
entry
.
scope
.
is_property_scope
and
(
not
self
.
entry
.
is_special
or
self
.
entry
.
wrapperbase_cname
)):
(
not
entry
.
is_special
or
entry
.
wrapperbase_cname
)):
# h_code = code.globalstate['h_code']
# h_code = code.globalstate['h_code']
docstr
=
self
.
entry
.
doc
docstr
=
entry
.
doc
if
docstr
.
is_unicode
:
if
docstr
.
is_unicode
:
docstr
=
docstr
.
utf8encode
()
docstr
=
docstr
.
utf8encode
()
code
.
putln
(
code
.
putln
(
'static char %s[] = "%s";'
%
(
'static char %s[] = "%s";'
%
(
self
.
entry
.
doc_cname
,
entry
.
doc_cname
,
split_string_literal
(
escape_byte_string
(
docstr
))))
split_string_literal
(
escape_byte_string
(
docstr
))))
if
self
.
entry
.
is_special
:
if
entry
.
is_special
:
code
.
putln
(
code
.
putln
(
"struct wrapperbase %s;"
%
self
.
entry
.
wrapperbase_cname
)
"struct wrapperbase %s;"
%
entry
.
wrapperbase_cname
)
if
with_pymethdef
or
self
.
fused_py_func
:
if
with_pymethdef
or
self
.
target
.
fused_py_func
:
code
.
put
(
code
.
put
(
"static PyMethodDef %s = "
%
"static PyMethodDef %s = "
%
self
.
entry
.
pymethdef_cname
)
entry
.
pymethdef_cname
)
code
.
put_pymethoddef
(
self
.
entry
,
";"
,
allow_skip
=
False
)
code
.
put_pymethoddef
(
self
.
target
.
entry
,
";"
,
allow_skip
=
False
)
code
.
putln
(
"%s {"
%
header
)
code
.
putln
(
"%s {"
%
header
)
def
generate_argument_declarations
(
self
,
env
,
code
):
def
generate_argument_declarations
(
self
,
env
,
code
):
for
arg
in
self
.
args
:
for
arg
in
self
.
args
:
if
arg
.
is_generic
:
# or arg.needs_conversion:
if
arg
.
is_generic
:
if
arg
.
needs_conversion
:
if
arg
.
needs_conversion
:
code
.
putln
(
"PyObject *%s = 0;"
%
arg
.
hdr_cname
)
code
.
putln
(
"PyObject *%s = 0;"
%
arg
.
hdr_cname
)
el
if
not
arg
.
entry
.
in_closur
e
:
el
s
e
:
code
.
put_var_declaration
(
arg
.
entry
)
code
.
put_var_declaration
(
arg
.
entry
)
for
entry
in
env
.
var_entries
:
if
entry
.
is_arg
:
code
.
put_var_declaration
(
entry
)
def
generate_keyword_list
(
self
,
code
):
def
generate_keyword_list
(
self
,
code
):
if
self
.
signature_has_generic_args
()
and
\
if
self
.
signature_has_generic_args
()
and
\
...
@@ -3159,7 +3314,7 @@ class DefNode(FuncDefNode):
...
@@ -3159,7 +3314,7 @@ class DefNode(FuncDefNode):
def
generate_argument_parsing_code
(
self
,
env
,
code
):
def
generate_argument_parsing_code
(
self
,
env
,
code
):
# Generate fast equivalent of PyArg_ParseTuple call for
# Generate fast equivalent of PyArg_ParseTuple call for
# generic arguments, if any, including args/kwargs
# generic arguments, if any, including args/kwargs
if
self
.
entry
.
signature
.
has_dummy_arg
and
not
self
.
self_in_stararg
:
if
self
.
signature
.
has_dummy_arg
and
not
self
.
self_in_stararg
:
# get rid of unused argument warning
# get rid of unused argument warning
code
.
putln
(
"%s = %s;"
%
(
Naming
.
self_cname
,
Naming
.
self_cname
))
code
.
putln
(
"%s = %s;"
%
(
Naming
.
self_cname
,
Naming
.
self_cname
))
...
@@ -3175,9 +3330,6 @@ class DefNode(FuncDefNode):
...
@@ -3175,9 +3330,6 @@ class DefNode(FuncDefNode):
if
not
arg
.
type
.
is_pyobject
:
if
not
arg
.
type
.
is_pyobject
:
if
not
arg
.
type
.
create_from_py_utility_code
(
env
):
if
not
arg
.
type
.
create_from_py_utility_code
(
env
):
pass
# will fail later
pass
# will fail later
elif
arg
.
is_self_arg
and
arg
.
entry
.
in_closure
:
# must store 'self' in the closure explicitly for extension types
self
.
generate_arg_assignment
(
arg
,
arg
.
hdr_cname
,
code
)
if
not
self
.
signature_has_generic_args
():
if
not
self
.
signature_has_generic_args
():
if
has_star_or_kw_args
:
if
has_star_or_kw_args
:
...
@@ -3220,41 +3372,17 @@ class DefNode(FuncDefNode):
...
@@ -3220,41 +3372,17 @@ class DefNode(FuncDefNode):
code
.
put_var_xdecref_clear
(
self
.
starstar_arg
.
entry
)
code
.
put_var_xdecref_clear
(
self
.
starstar_arg
.
entry
)
else
:
else
:
code
.
put_var_decref_clear
(
self
.
starstar_arg
.
entry
)
code
.
put_var_decref_clear
(
self
.
starstar_arg
.
entry
)
code
.
put_add_traceback
(
self
.
entry
.
qualified_name
)
code
.
put_add_traceback
(
self
.
target
.
entry
.
qualified_name
)
# The arguments are put into the closure one after the
# other, so when type errors are found, all references in
# the closure instance must be properly ref-counted to
# facilitate generic closure instance deallocation. In
# the case of an argument type error, it's best to just
# DECREF+clear the already handled references, as this
# frees their references as early as possible.
for
arg
in
self
.
args
:
if
arg
.
type
.
is_pyobject
and
arg
.
entry
.
in_closure
:
code
.
put_var_xdecref_clear
(
arg
.
entry
)
if
self
.
needs_closure
:
code
.
put_decref
(
Naming
.
cur_scope_cname
,
self
.
local_scope
.
scope_class
.
type
)
code
.
put_finish_refcount_context
()
code
.
put_finish_refcount_context
()
code
.
putln
(
"return %s;"
%
self
.
error_value
())
code
.
putln
(
"return %s;"
%
self
.
error_value
())
if
code
.
label_used
(
end_label
):
if
code
.
label_used
(
end_label
):
code
.
put_label
(
end_label
)
code
.
put_label
(
end_label
)
# fix refnanny view on closure variables here, instead of
# doing it separately for each arg parsing special case
if
self
.
star_arg
and
self
.
star_arg
.
entry
.
in_closure
:
code
.
put_var_giveref
(
self
.
star_arg
.
entry
)
if
self
.
starstar_arg
and
self
.
starstar_arg
.
entry
.
in_closure
:
code
.
put_var_giveref
(
self
.
starstar_arg
.
entry
)
for
arg
in
self
.
args
:
if
arg
.
type
.
is_pyobject
and
arg
.
entry
.
in_closure
:
code
.
put_var_giveref
(
arg
.
entry
)
def
generate_arg_assignment
(
self
,
arg
,
item
,
code
,
incref_closure
=
True
):
def
generate_arg_assignment
(
self
,
arg
,
item
,
code
,
incref_closure
=
True
):
if
arg
.
type
.
is_pyobject
:
if
arg
.
type
.
is_pyobject
:
if
arg
.
is_generic
:
if
arg
.
is_generic
:
item
=
PyrexTypes
.
typecast
(
arg
.
type
,
PyrexTypes
.
py_object_type
,
item
)
item
=
PyrexTypes
.
typecast
(
arg
.
type
,
PyrexTypes
.
py_object_type
,
item
)
entry
=
arg
.
entry
entry
=
arg
.
entry
if
incref_closure
and
entry
.
in_closure
:
code
.
put_incref
(
item
,
PyrexTypes
.
py_object_type
)
code
.
putln
(
"%s = %s;"
%
(
entry
.
cname
,
item
))
code
.
putln
(
"%s = %s;"
%
(
entry
.
cname
,
item
))
else
:
else
:
func
=
arg
.
type
.
from_py_function
func
=
arg
.
type
.
from_py_function
...
@@ -3509,8 +3637,6 @@ class DefNode(FuncDefNode):
...
@@ -3509,8 +3637,6 @@ class DefNode(FuncDefNode):
code
.
putln
(
"if (unlikely(!%s)) {"
%
self
.
star_arg
.
entry
.
cname
)
code
.
putln
(
"if (unlikely(!%s)) {"
%
self
.
star_arg
.
entry
.
cname
)
if
self
.
starstar_arg
:
if
self
.
starstar_arg
:
code
.
put_decref_clear
(
self
.
starstar_arg
.
entry
.
cname
,
py_object_type
)
code
.
put_decref_clear
(
self
.
starstar_arg
.
entry
.
cname
,
py_object_type
)
if
self
.
needs_closure
:
code
.
put_decref
(
Naming
.
cur_scope_cname
,
self
.
local_scope
.
scope_class
.
type
)
code
.
put_finish_refcount_context
()
code
.
put_finish_refcount_context
()
code
.
putln
(
'return %s;'
%
self
.
error_value
())
code
.
putln
(
'return %s;'
%
self
.
error_value
())
code
.
putln
(
'}'
)
code
.
putln
(
'}'
)
...
@@ -3527,10 +3653,10 @@ class DefNode(FuncDefNode):
...
@@ -3527,10 +3653,10 @@ class DefNode(FuncDefNode):
code
.
putln
(
"PyObject* values[%d] = {%s};"
%
(
code
.
putln
(
"PyObject* values[%d] = {%s};"
%
(
max_args
,
','
.
join
(
'0'
*
max_args
)))
max_args
,
','
.
join
(
'0'
*
max_args
)))
if
self
.
defaults_struct
:
if
self
.
target
.
defaults_struct
:
code
.
putln
(
'%s *%s = __Pyx_CyFunction_Defaults(%s, %s);'
%
(
code
.
putln
(
'%s *%s = __Pyx_CyFunction_Defaults(%s, %s);'
%
(
self
.
defaults_struct
,
Naming
.
dynamic_args_cname
,
self
.
target
.
defaults_struct
,
Naming
.
dynamic_args_cname
,
self
.
defaults_struct
,
Naming
.
self_cname
))
self
.
target
.
defaults_struct
,
Naming
.
self_cname
))
# assign borrowed Python default values to the values array,
# assign borrowed Python default values to the values array,
# so that they can be overwritten by received arguments below
# so that they can be overwritten by received arguments below
...
@@ -3697,10 +3823,6 @@ class DefNode(FuncDefNode):
...
@@ -3697,10 +3823,6 @@ class DefNode(FuncDefNode):
for
arg
in
self
.
args
:
for
arg
in
self
.
args
:
if
arg
.
needs_conversion
:
if
arg
.
needs_conversion
:
self
.
generate_arg_conversion
(
arg
,
code
)
self
.
generate_arg_conversion
(
arg
,
code
)
elif
not
arg
.
is_self_arg
and
arg
.
entry
.
in_closure
:
if
arg
.
type
.
is_pyobject
:
code
.
put_incref
(
arg
.
hdr_cname
,
py_object_type
)
code
.
putln
(
'%s = %s;'
%
(
arg
.
entry
.
cname
,
arg
.
hdr_cname
))
def
generate_arg_conversion
(
self
,
arg
,
code
):
def
generate_arg_conversion
(
self
,
arg
,
code
):
# Generate conversion code for one argument.
# Generate conversion code for one argument.
...
@@ -3768,10 +3890,7 @@ class DefNode(FuncDefNode):
...
@@ -3768,10 +3890,7 @@ class DefNode(FuncDefNode):
self
.
generate_arg_none_check
(
arg
,
code
)
self
.
generate_arg_none_check
(
arg
,
code
)
def
error_value
(
self
):
def
error_value
(
self
):
return
self
.
entry
.
signature
.
error_value
return
self
.
signature
.
error_value
def
caller_will_check_exceptions
(
self
):
return
1
class
GeneratorDefNode
(
DefNode
):
class
GeneratorDefNode
(
DefNode
):
...
@@ -7837,6 +7956,8 @@ class CnameDecoratorNode(StatNode):
...
@@ -7837,6 +7956,8 @@ class CnameDecoratorNode(StatNode):
e
.
cname
=
self
.
cname
e
.
cname
=
self
.
cname
e
.
func_cname
=
self
.
cname
e
.
func_cname
=
self
.
cname
e
.
used
=
True
e
.
used
=
True
if
e
.
pyfunc_cname
and
'.'
in
e
.
pyfunc_cname
:
e
.
pyfunc_cname
=
self
.
mangle
(
e
.
pyfunc_cname
)
elif
is_struct_or_enum
:
elif
is_struct_or_enum
:
e
.
cname
=
e
.
type
.
cname
=
self
.
cname
e
.
cname
=
e
.
type
.
cname
=
self
.
cname
else
:
else
:
...
@@ -7853,12 +7974,16 @@ class CnameDecoratorNode(StatNode):
...
@@ -7853,12 +7974,16 @@ class CnameDecoratorNode(StatNode):
for
name
,
entry
in
scope
.
entries
.
iteritems
():
for
name
,
entry
in
scope
.
entries
.
iteritems
():
if
entry
.
func_cname
:
if
entry
.
func_cname
:
cname
=
entry
.
cname
entry
.
func_cname
=
self
.
mangle
(
entry
.
cname
)
if
'.'
in
cname
:
if
entry
.
pyfunc_cname
:
# remove __pyx_base from func_cname
old
=
entry
.
pyfunc_cname
cname
=
cname
.
split
(
'.'
)[
-
1
]
entry
.
pyfunc_cname
=
self
.
mangle
(
entry
.
pyfunc_cname
)
entry
.
func_cname
=
'%s_%s'
%
(
self
.
cname
,
cname
)
def
mangle
(
self
,
cname
):
if
'.'
in
cname
:
# remove __pyx_base from func_cname
cname
=
cname
.
split
(
'.'
)[
-
1
]
return
'%s_%s'
%
(
self
.
cname
,
cname
)
def
analyse_expressions
(
self
,
env
):
def
analyse_expressions
(
self
,
env
):
self
.
node
.
analyse_expressions
(
env
)
self
.
node
.
analyse_expressions
(
env
)
...
...
Cython/Compiler/Optimize.py
View file @
b22011f8
...
@@ -1643,6 +1643,28 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
...
@@ -1643,6 +1643,28 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
return
node
return
node
return
kwargs
return
kwargs
class
InlineDefNodeCalls
(
Visitor
.
CythonTransform
):
visit_Node
=
Visitor
.
VisitorTransform
.
recurse_to_children
def
visit_SimpleCallNode
(
self
,
node
):
self
.
visitchildren
(
node
)
if
not
self
.
current_directives
.
get
(
'optimize.inline_defnode_calls'
):
return
node
function_name
=
node
.
function
if
not
function_name
.
is_name
:
return
node
if
not
function_name
.
cf_state
.
is_single
:
return
node
function
=
function_name
.
cf_state
.
one
().
rhs
if
not
isinstance
(
function
,
ExprNodes
.
PyCFunctionNode
):
return
node
inlined
=
ExprNodes
.
InlinedDefNodeCallNode
(
node
.
pos
,
function_name
=
function_name
,
function
=
function
,
args
=
node
.
args
)
if
inlined
.
can_be_inlined
():
return
inlined
return
node
class
OptimizeBuiltinCalls
(
Visitor
.
EnvTransform
):
class
OptimizeBuiltinCalls
(
Visitor
.
EnvTransform
):
"""Optimize some common methods calls and instantiation patterns
"""Optimize some common methods calls and instantiation patterns
...
...
Cython/Compiler/Options.py
View file @
b22011f8
...
@@ -106,6 +106,9 @@ directive_defaults = {
...
@@ -106,6 +106,9 @@ directive_defaults = {
'warn.unused_arg'
:
False
,
'warn.unused_arg'
:
False
,
'warn.unused_result'
:
False
,
'warn.unused_result'
:
False
,
# optimizations
'optimize.inline_defnode_calls'
:
False
,
# remove unreachable code
# remove unreachable code
'remove_unreachable'
:
True
,
'remove_unreachable'
:
True
,
...
...
Cython/Compiler/ParseTreeTransforms.py
View file @
b22011f8
...
@@ -1493,6 +1493,9 @@ if VALUE is not None:
...
@@ -1493,6 +1493,9 @@ if VALUE is not None:
if
node
.
py_func
:
if
node
.
py_func
:
node
.
stats
.
insert
(
0
,
node
.
py_func
)
node
.
stats
.
insert
(
0
,
node
.
py_func
)
self
.
visit
(
node
.
py_func
)
if
node
.
py_func
.
needs_assignment_synthesis
(
env
):
node
=
[
node
,
self
.
_synthesize_assignment
(
node
.
py_func
,
env
)]
else
:
else
:
node
.
body
.
analyse_declarations
(
lenv
)
node
.
body
.
analyse_declarations
(
lenv
)
...
@@ -1514,6 +1517,54 @@ if VALUE is not None:
...
@@ -1514,6 +1517,54 @@ if VALUE is not None:
self
.
seen_vars_stack
.
pop
()
self
.
seen_vars_stack
.
pop
()
return
node
return
node
def
visit_DefNode
(
self
,
node
):
node
=
self
.
visit_FuncDefNode
(
node
)
env
=
self
.
env_stack
[
-
1
]
if
(
not
isinstance
(
node
,
Nodes
.
DefNode
)
or
node
.
fused_py_func
or
node
.
is_generator_body
or
not
node
.
needs_assignment_synthesis
(
env
)):
return
node
return
[
node
,
self
.
_synthesize_assignment
(
node
,
env
)]
def
_synthesize_assignment
(
self
,
node
,
env
):
# Synthesize assignment node and put it right after defnode
genv
=
env
while
genv
.
is_py_class_scope
or
genv
.
is_c_class_scope
:
genv
=
genv
.
outer_scope
if
genv
.
is_closure_scope
:
rhs
=
node
.
py_cfunc_node
=
ExprNodes
.
InnerFunctionNode
(
node
.
pos
,
def_node
=
node
,
pymethdef_cname
=
node
.
entry
.
pymethdef_cname
,
code_object
=
ExprNodes
.
CodeObjectNode
(
node
))
else
:
rhs
=
ExprNodes
.
PyCFunctionNode
(
node
.
pos
,
def_node
=
node
,
pymethdef_cname
=
node
.
entry
.
pymethdef_cname
,
binding
=
self
.
current_directives
.
get
(
'binding'
),
specialized_cpdefs
=
node
.
specialized_cpdefs
,
code_object
=
ExprNodes
.
CodeObjectNode
(
node
))
if
env
.
is_py_class_scope
:
rhs
.
binding
=
True
node
.
is_cyfunction
=
rhs
.
binding
if
node
.
decorators
:
for
decorator
in
node
.
decorators
[::
-
1
]:
rhs
=
ExprNodes
.
SimpleCallNode
(
decorator
.
pos
,
function
=
decorator
.
decorator
,
args
=
[
rhs
])
assmt
=
Nodes
.
SingleAssignmentNode
(
node
.
pos
,
lhs
=
ExprNodes
.
NameNode
(
node
.
pos
,
name
=
node
.
name
),
rhs
=
rhs
)
assmt
.
analyse_declarations
(
env
)
return
assmt
def
visit_ScopedExprNode
(
self
,
node
):
def
visit_ScopedExprNode
(
self
,
node
):
env
=
self
.
env_stack
[
-
1
]
env
=
self
.
env_stack
[
-
1
]
node
.
analyse_declarations
(
env
)
node
.
analyse_declarations
(
env
)
...
@@ -1645,11 +1696,13 @@ if VALUE is not None:
...
@@ -1645,11 +1696,13 @@ if VALUE is not None:
return
None
return
None
def
visit_CnameDecoratorNode
(
self
,
node
):
def
visit_CnameDecoratorNode
(
self
,
node
):
self
.
visitchildren
(
node
)
child_node
=
self
.
visit
(
node
.
node
)
if
not
child_node
:
if
not
node
.
node
:
return
None
return
None
if
type
(
child_node
)
is
list
:
# Assignment synthesized
node
.
child_node
=
child_node
[
0
]
return
[
node
]
+
child_node
[
1
:]
node
.
node
=
child_node
return
node
return
node
def
create_Property
(
self
,
entry
):
def
create_Property
(
self
,
entry
):
...
@@ -2065,9 +2118,6 @@ class CreateClosureClasses(CythonTransform):
...
@@ -2065,9 +2118,6 @@ class CreateClosureClasses(CythonTransform):
return
from_closure
,
in_closure
return
from_closure
,
in_closure
def
create_class_from_scope
(
self
,
node
,
target_module_scope
,
inner_node
=
None
):
def
create_class_from_scope
(
self
,
node
,
target_module_scope
,
inner_node
=
None
):
# skip generator body
if
node
.
is_generator_body
:
return
# move local variables into closure
# move local variables into closure
if
node
.
is_generator
:
if
node
.
is_generator
:
for
entry
in
node
.
local_scope
.
entries
.
values
():
for
entry
in
node
.
local_scope
.
entries
.
values
():
...
@@ -2160,6 +2210,10 @@ class CreateClosureClasses(CythonTransform):
...
@@ -2160,6 +2210,10 @@ class CreateClosureClasses(CythonTransform):
self
.
path
.
pop
()
self
.
path
.
pop
()
return
node
return
node
def
visit_GeneratorBodyDefNode
(
self
,
node
):
self
.
visitchildren
(
node
)
return
node
def
visit_CFuncDefNode
(
self
,
node
):
def
visit_CFuncDefNode
(
self
,
node
):
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
return
node
return
node
...
...
Cython/Compiler/Pipeline.py
View file @
b22011f8
...
@@ -137,6 +137,7 @@ def create_pipeline(context, mode, exclude_classes=()):
...
@@ -137,6 +137,7 @@ def create_pipeline(context, mode, exclude_classes=()):
from
AutoDocTransforms
import
EmbedSignature
from
AutoDocTransforms
import
EmbedSignature
from
Optimize
import
FlattenInListTransform
,
SwitchTransform
,
IterationTransform
from
Optimize
import
FlattenInListTransform
,
SwitchTransform
,
IterationTransform
from
Optimize
import
EarlyReplaceBuiltinCalls
,
OptimizeBuiltinCalls
from
Optimize
import
EarlyReplaceBuiltinCalls
,
OptimizeBuiltinCalls
from
Optimize
import
InlineDefNodeCalls
from
Optimize
import
ConstantFolding
,
FinalOptimizePhase
from
Optimize
import
ConstantFolding
,
FinalOptimizePhase
from
Optimize
import
DropRefcountingTransform
from
Optimize
import
DropRefcountingTransform
from
Buffer
import
IntroduceBufferAuxiliaryVars
from
Buffer
import
IntroduceBufferAuxiliaryVars
...
@@ -185,6 +186,7 @@ def create_pipeline(context, mode, exclude_classes=()):
...
@@ -185,6 +186,7 @@ def create_pipeline(context, mode, exclude_classes=()):
MarkOverflowingArithmetic
(
context
),
MarkOverflowingArithmetic
(
context
),
IntroduceBufferAuxiliaryVars
(
context
),
IntroduceBufferAuxiliaryVars
(
context
),
_check_c_declarations
,
_check_c_declarations
,
InlineDefNodeCalls
(
context
),
AnalyseExpressionsTransform
(
context
),
AnalyseExpressionsTransform
(
context
),
FindInvalidUseOfFusedTypes
(
context
),
FindInvalidUseOfFusedTypes
(
context
),
CreateClosureClasses
(
context
),
## After all lookups and type inference
CreateClosureClasses
(
context
),
## After all lookups and type inference
...
...
Cython/Compiler/Symtab.py
View file @
b22011f8
...
@@ -156,6 +156,7 @@ class Entry(object):
...
@@ -156,6 +156,7 @@ class Entry(object):
from_closure = 0
from_closure = 0
is_declared_generic = 0
is_declared_generic = 0
is_readonly = 0
is_readonly = 0
pyfunc_cname = None
func_cname = None
func_cname = None
func_modifiers = []
func_modifiers = []
final_func_cname = None
final_func_cname = None
...
...
tests/run/closure_inlining.pyx
0 → 100644
View file @
b22011f8
# cython: optimize.inline_defnode_calls=True
# mode: run
cimport
cython
@
cython
.
test_fail_if_path_exists
(
'//SimpleCallNode'
)
@
cython
.
test_assert_path_exists
(
'//InlinedDefNodeCallNode'
)
def
simple_noargs
():
"""
>>> simple_noargs()
123
"""
def
inner
():
return
123
return
inner
()
@
cython
.
test_fail_if_path_exists
(
'//SimpleCallNode'
)
@
cython
.
test_assert_path_exists
(
'//InlinedDefNodeCallNode'
)
def
test_coerce
(
a
,
int
b
):
"""
>>> test_coerce(2, 2)
4
"""
def
inner
(
int
a
,
b
):
return
a
*
b
return
inner
(
a
,
b
)
cdef
class
Foo
(
object
):
def
__repr__
(
self
):
return
'<Foo>'
@
cython
.
test_fail_if_path_exists
(
'//SimpleCallNode'
)
@
cython
.
test_assert_path_exists
(
'//InlinedDefNodeCallNode'
)
def
test_func_signature
(
a
):
"""
>>> test_func_signature(Foo())
<Foo>
"""
def
inner
(
Foo
a
):
return
a
return
inner
(
a
)
@
cython
.
test_fail_if_path_exists
(
'//SimpleCallNode'
)
@
cython
.
test_assert_path_exists
(
'//InlinedDefNodeCallNode'
)
def
test_func_signature2
(
a
,
b
):
"""
>>> test_func_signature2(Foo(), 123)
(<Foo>, 123)
"""
def
inner
(
Foo
a
,
b
):
return
a
,
b
return
inner
(
a
,
b
)
# Starred args and default values are not yet supported for inlining
@
cython
.
test_assert_path_exists
(
'//SimpleCallNode'
)
def
test_defaults
(
a
,
b
):
"""
>>> test_defaults(1, 2)
(1, 2, 123)
"""
def
inner
(
a
,
b
=
b
,
c
=
123
):
return
a
,
b
,
c
return
inner
(
a
)
@
cython
.
test_assert_path_exists
(
'//SimpleCallNode'
)
def
test_starred
(
a
):
"""
>>> test_starred(123)
(123, (), {})
"""
def
inner
(
a
,
*
args
,
**
kwargs
):
return
a
,
args
,
kwargs
return
inner
(
a
)
tests/run/type_inference.pyx
View file @
b22011f8
...
@@ -406,10 +406,10 @@ cdef object some_float_value():
...
@@ -406,10 +406,10 @@ cdef object some_float_value():
return
2.0
return
2.0
@
cython
.
test_fail_if_path_exists
(
'//NameNode[@type.is_pyobject = True]'
)
@
cython
.
test_assert_path_exists
(
'//NameNode[@type.is_pyobject]'
,
'//NameNode[@type.is_pyobject = False]'
)
@
infer_types
(
None
)
@
infer_types
(
None
)
@
cython
.
test_fail_if_path_exists
(
'//DefNode//NameNode[@type.is_pyobject = True]'
)
@
cython
.
test_assert_path_exists
(
'//DefNode//NameNode[@type.is_pyobject]'
,
'//DefNode//NameNode[@type.is_pyobject = False]'
)
def
double_loop
():
def
double_loop
():
"""
"""
>>> double_loop() == 1.0 * 10
>>> double_loop() == 1.0 * 10
...
...
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