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
Gwenaël Samain
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):
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
):
subexprs
=
[]
def
__init__
(
self
,
pos
,
py_name
,
cname
,
func_type
,
utility_code
=
None
):
...
...
@@ -6252,15 +6349,16 @@ class GeneratorExpressionNode(LambdaNode):
super
(
GeneratorExpressionNode
,
self
).
analyse_declarations
(
env
)
# No pymethdef required
self
.
def_node
.
pymethdef_required
=
False
self
.
def_node
.
py_wrapper_required
=
False
self
.
def_node
.
is_cyfunction
=
False
# Force genexpr signature
self
.
def_node
.
entry
.
signature
=
TypeSlots
.
pyfunction_noargs
def
generate_result_code
(
self
,
code
):
code
.
putln
(
'%s = %s(%s
, NULL
); %s'
%
(
'%s = %s(%s); %s'
%
(
self
.
result
(),
self
.
def_node
.
entry
.
func_cname
,
self
.
def_node
.
entry
.
py
func_cname
,
self
.
self_result_code
(),
code
.
error_goto_if_null
(
self
.
result
(),
self
.
pos
)))
code
.
put_gotref
(
self
.
py_result
())
...
...
Cython/Compiler/FlowControl.py
View file @
b22011f8
...
...
@@ -327,6 +327,32 @@ class NameReference(object):
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
):
"""Graphviz subgraph object."""
...
...
@@ -530,11 +556,10 @@ def check_definitions(flow, compiler_directives):
messages
.
report
()
# Remove Uninitialized from cf_state
for
node
in
assmt_nodes
:
node
.
cf_state
.
discard
(
Uninitialized
)
node
.
cf_state
=
ControlFlowState
(
node
.
cf_state
)
for
node
in
references
:
node
.
cf_state
.
discard
(
Uninitialized
)
node
.
cf_state
=
ControlFlowState
(
node
.
cf_state
)
class
AssignmentCollector
(
TreeVisitor
):
...
...
@@ -632,13 +657,7 @@ class ControlFlowAnalysis(CythonTransform):
return
node
def
visit_DefNode
(
self
,
node
):
## XXX: no target name node here
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
)
def
visit_GeneratorBodyDefNode
(
self
,
node
):
...
...
Cython/Compiler/Naming.py
View file @
b22011f8
...
...
@@ -19,6 +19,7 @@ funcdoc_prefix = pyrex_prefix + "doc_"
enum_prefix
=
pyrex_prefix
+
"e_"
func_prefix
=
pyrex_prefix
+
"f_"
pyfunc_prefix
=
pyrex_prefix
+
"pf_"
pywrap_prefix
=
pyrex_prefix
+
"pw_"
genbody_prefix
=
pyrex_prefix
+
"gb_"
gstab_prefix
=
pyrex_prefix
+
"getsets_"
prop_get_prefix
=
pyrex_prefix
+
"getprop_"
...
...
Cython/Compiler/Nodes.py
View file @
b22011f8
...
...
@@ -1308,7 +1308,6 @@ class FuncDefNode(StatNode, BlockNode):
# with fused argument types with a FusedCFuncDefNode
py_func
=
None
assmt
=
None
needs_closure
=
False
needs_outer_scope
=
False
pymethdef_required
=
False
...
...
@@ -1405,14 +1404,7 @@ class FuncDefNode(StatNode, BlockNode):
if
'cython_unused'
not
in
self
.
modifiers
:
self
.
modifiers
=
self
.
modifiers
+
[
'cython_unused'
]
preprocessor_guard
=
None
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
preprocessor_guard
=
self
.
get_preprocessor_guard
()
profile
=
code
.
globalstate
.
directives
[
'profile'
]
if
profile
and
lenv
.
nogil
:
...
...
@@ -1459,7 +1451,7 @@ class FuncDefNode(StatNode, BlockNode):
self
.
generate_argument_declarations
(
lenv
,
code
)
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
)
# Initialize the return variable __pyx_r
...
...
@@ -1555,7 +1547,8 @@ class FuncDefNode(StatNode, BlockNode):
is_cdef
=
isinstance
(
self
,
CFuncDefNode
)
for
entry
in
lenv
.
arg_entries
:
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
)
# Note: defaults are always increffed. For def functions, we
...
...
@@ -1565,6 +1558,9 @@ class FuncDefNode(StatNode, BlockNode):
if
is_cdef
and
entry
.
type
.
is_memoryviewslice
:
code
.
put_incref_memoryviewslice
(
entry
.
cname
,
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
for
entry
in
lenv
.
var_entries
+
lenv
.
arg_entries
:
...
...
@@ -1709,14 +1705,15 @@ class FuncDefNode(StatNode, BlockNode):
if
entry
.
type
.
is_memoryviewslice
:
code
.
put_xdecref_memoryviewslice
(
entry
.
cname
,
have_gil
=
not
lenv
.
nogil
)
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
for
entry
in
lenv
.
arg_entries
:
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
)
if
entry
.
type
.
is_memoryviewslice
:
code
.
put_xdecref_memoryviewslice
(
entry
.
cname
,
...
...
@@ -1820,9 +1817,6 @@ class FuncDefNode(StatNode, BlockNode):
for
arg
in
self
.
args
:
if
not
arg
.
is_dynamic
:
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
...
...
@@ -1853,6 +1847,21 @@ class FuncDefNode(StatNode, BlockNode):
code
.
putln
(
"__Pyx_DECREF(Py_None); %s->obj = NULL;"
%
info
)
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
):
# C function definition.
#
...
...
@@ -2548,7 +2557,9 @@ def __pyx_fused_cpdef(signatures, args, kwargs):
py_func
.
analyse_declarations
(
env
)
# ... and its body
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
...
...
@@ -2607,6 +2618,8 @@ class PyArgDeclNode(Node):
# entry Symtab.Entry
# annotation ExprNode or None Py3 argument annotation
child_attrs
=
[]
is_self_arg
=
False
is_type_arg
=
False
def
generate_function_definitions
(
self
,
env
,
code
):
self
.
entry
.
generate_function_definitions
(
env
,
code
)
...
...
@@ -2633,8 +2646,6 @@ class DefNode(FuncDefNode):
# The following subnode is constructed internally
# when the def statement is inside a Python class definition.
#
# assmt AssignmentNode Function construction/assignment
#
# fused_py_func DefNode The original fused cpdef DefNode
# (in case this is a specialization)
# specialized_cpdefs [DefNode] list of specialized cpdef DefNodes
...
...
@@ -2645,9 +2656,6 @@ class DefNode(FuncDefNode):
child_attrs
=
[
"args"
,
"star_arg"
,
"starstar_arg"
,
"body"
,
"decorators"
]
lambda_name
=
None
assmt
=
None
num_kwonly_args
=
0
num_required_kw_args
=
0
reqd_kw_flags_cname
=
"0"
is_wrapper
=
0
no_assignment_synthesis
=
0
...
...
@@ -2663,6 +2671,9 @@ class DefNode(FuncDefNode):
fused_py_func
=
False
specialized_cpdefs
=
None
py_wrapper
=
None
py_wrapper_required
=
True
func_cname
=
None
def
__init__
(
self
,
pos
,
**
kwds
):
FuncDefNode
.
__init__
(
self
,
pos
,
**
kwds
)
...
...
@@ -2781,6 +2792,16 @@ class DefNode(FuncDefNode):
self
.
return_type
=
self
.
entry
.
signature
.
return_type
()
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
):
directive_locals
=
self
.
directive_locals
=
env
.
directives
[
'locals'
]
allow_none_for_extension_args
=
env
.
directives
[
'allow_none_for_extension_args'
]
...
...
@@ -2926,17 +2947,6 @@ class DefNode(FuncDefNode):
"(%d declared, %s expected)"
%
(
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
):
#print "DefNode.declare_pyfunction:", self.name, "in", env ###
name
=
self
.
name
...
...
@@ -2950,8 +2960,7 @@ class DefNode(FuncDefNode):
entry
=
env
.
declare_pyfunction
(
name
,
self
.
pos
,
allow_redefine
=
not
self
.
is_wrapper
)
self
.
entry
=
entry
prefix
=
env
.
next_id
(
env
.
scope_prefix
)
entry
.
func_cname
=
Naming
.
pyfunc_prefix
+
prefix
+
name
entry
.
pymethdef_cname
=
Naming
.
pymethdef_prefix
+
prefix
+
name
self
.
entry
.
pyfunc_cname
=
Naming
.
pyfunc_prefix
+
prefix
+
name
if
Options
.
docstrings
:
entry
.
doc
=
embed_position
(
self
.
pos
,
self
.
doc
)
entry
.
doc_cname
=
Naming
.
funcdoc_prefix
+
prefix
+
name
...
...
@@ -2967,6 +2976,7 @@ class DefNode(FuncDefNode):
entry
=
env
.
declare_lambda_function
(
self
.
lambda_name
,
self
.
pos
)
entry
.
doc
=
None
self
.
entry
=
entry
self
.
entry
.
pyfunc_cname
=
entry
.
cname
def
declare_arguments
(
self
,
env
):
for
arg
in
self
.
args
:
...
...
@@ -2981,10 +2991,6 @@ class DefNode(FuncDefNode):
arg
.
entry
.
is_arg
=
1
arg
.
entry
.
used
=
1
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
.
starstar_arg
)
...
...
@@ -3005,14 +3011,13 @@ class DefNode(FuncDefNode):
self
.
local_scope
.
directives
=
env
.
directives
self
.
analyse_default_values
(
env
)
if
self
.
needs_assignment_synthesis
(
env
):
# Shouldn't we be doing this at the module level too?
self
.
synthesize_assignment_node
(
env
)
elif
self
.
decorators
:
if
not
self
.
needs_assignment_synthesis
(
env
)
and
self
.
decorators
:
for
decorator
in
self
.
decorators
[::
-
1
]:
decorator
.
decorator
.
analyse_expressions
(
env
)
def
needs_assignment_synthesis
(
self
,
env
,
code
=
None
):
if
self
.
is_wrapper
:
return
False
if
self
.
specialized_cpdefs
or
self
.
is_staticmethod
:
return
True
if
self
.
no_assignment_synthesis
:
...
...
@@ -3027,122 +3032,272 @@ class DefNode(FuncDefNode):
return
code
.
globalstate
.
directives
[
'binding'
]
return
env
.
is_py_class_scope
or
env
.
is_closure_scope
def
synthesize_assignment_node
(
self
,
env
):
import
ExprNodes
def
error_value
(
self
):
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
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
while
genv
.
is_py_class_scope
or
genv
.
is_c_class_scope
:
genv
=
genv
.
outer_scope
def
arg_decl_code
(
arg
):
entry
=
arg
.
entry
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
:
rhs
=
self
.
py_cfunc_node
=
ExprNodes
.
InnerFunctionNode
(
self
.
pos
,
def_node
=
self
,
pymethdef_cname
=
self
.
entry
.
pymethdef_cname
,
code_object
=
ExprNodes
.
CodeObjectNode
(
self
))
self
.
signature
=
target_entry
.
signature
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
.
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
:
r
hs
=
ExprNodes
.
PyCFunctionNode
(
self
.
pos
,
def_node
=
self
,
pymethdef_cname
=
self
.
entry
.
pymethdef_cname
,
binding
=
env
.
directives
[
'binding'
],
specialized_cpdefs
=
self
.
specialized_cpdefs
,
code_object
=
ExprNodes
.
CodeObjectNode
(
self
)
)
r
etval_init
=
''
if
not
self
.
return_type
.
is_void
:
code
.
putln
(
'%s%s;'
%
(
self
.
return_type
.
declaration_code
(
Naming
.
retval_cname
)
,
retval_init
))
code
.
put_declare_refcount_context
()
code
.
put_setup_refcount_context
(
'%s (wrapper)'
%
self
.
name
)
if
env
.
is_py_class_scope
:
rhs
.
binding
=
True
self
.
generate_argument_parsing_code
(
lenv
,
code
)
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
:
for
decorator
in
self
.
decorators
[::
-
1
]
:
rhs
=
ExprNodes
.
SimpleCallNode
(
decorator
.
pos
,
function
=
decorator
.
decorator
,
args
=
[
rhs
]
)
# ----- Error cleanup
if
code
.
error_label
in
code
.
labels_used
:
code
.
put_goto
(
code
.
return_label
)
code
.
put_label
(
code
.
error_label
)
for
cname
,
type
in
code
.
funcstate
.
all_managed_temps
():
code
.
put_xdecref
(
cname
,
type
)
self
.
assmt
=
SingleAssignmentNode
(
self
.
pos
,
lhs
=
ExprNodes
.
NameNode
(
self
.
pos
,
name
=
self
.
name
),
rhs
=
rhs
)
self
.
assmt
.
analyse_declarations
(
env
)
self
.
assmt
.
analyse_expressions
(
env
)
# ----- Non-error return cleanup
code
.
put_label
(
code
.
return_label
)
for
entry
in
lenv
.
var_entries
:
if
entry
.
is_arg
and
entry
.
type
.
is_pyobject
:
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
):
arg_code_list
=
[]
sig
=
self
.
entry
.
signature
sig
=
self
.
signature
if
sig
.
has_dummy_arg
or
self
.
self_in_stararg
:
arg_code_list
.
append
(
"PyObject *%s"
%
Naming
.
self_cname
)
for
arg
in
self
.
args
:
if
not
arg
.
is_generic
:
if
arg
.
is_self_arg
or
arg
.
is_type_arg
:
arg_code_list
.
append
(
"PyObject *%s"
%
arg
.
hdr_cname
)
else
:
decl
=
arg
.
hdr_type
.
declaration_code
(
arg
.
hdr_cname
)
entry
=
self
.
local_scope
.
lookup
(
arg
.
name
)
if
not
entry
.
cf_used
:
arg_code_list
.
append
(
'CYTHON_UNUSED '
+
decl
)
else
:
arg_code_list
.
append
(
decl
)
if
not
self
.
entry
.
is_special
and
sig
.
method_flags
()
==
[
TypeSlots
.
method_noargs
]:
arg_code_list
.
append
(
arg
.
hdr_type
.
declaration_code
(
arg
.
hdr_cname
))
entry
=
self
.
target
.
entry
if
not
entry
.
is_special
and
sig
.
method_flags
()
==
[
TypeSlots
.
method_noargs
]:
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"
)
if
sig
.
has_generic_args
:
arg_code_list
.
append
(
"PyObject *%s, PyObject *%s"
%
(
Naming
.
args_cname
,
Naming
.
kwds_cname
))
arg_code
=
", "
.
join
(
arg_code_list
)
dc
=
self
.
return_type
.
declaration_code
(
self
.
entry
.
func_cname
)
mf
=
" "
.
join
(
self
.
modifiers
).
upper
()
if
mf
:
mf
+=
" "
header
=
"static %s%s(%s)"
%
(
mf
,
dc
,
arg_code
)
dc
=
self
.
return_type
.
declaration_code
(
entry
.
func_cname
)
header
=
"static %s(%s)"
%
(
dc
,
arg_code
)
code
.
putln
(
"%s; /*proto*/"
%
header
)
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
# want the prototype for the "fused cpdef", in case we're
# 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
)
return
if
(
Options
.
docstrings
and
self
.
entry
.
doc
and
not
self
.
fused_py_func
and
not
self
.
entry
.
scope
.
is_property_scope
and
(
not
self
.
entry
.
is_special
or
self
.
entry
.
wrapperbase_cname
)):
if
(
Options
.
docstrings
and
entry
.
doc
and
not
self
.
target
.
fused_py_func
and
not
entry
.
scope
.
is_property_scope
and
(
not
entry
.
is_special
or
entry
.
wrapperbase_cname
)):
# h_code = code.globalstate['h_code']
docstr
=
self
.
entry
.
doc
docstr
=
entry
.
doc
if
docstr
.
is_unicode
:
docstr
=
docstr
.
utf8encode
()
code
.
putln
(
'static char %s[] = "%s";'
%
(
self
.
entry
.
doc_cname
,
entry
.
doc_cname
,
split_string_literal
(
escape_byte_string
(
docstr
))))
if
self
.
entry
.
is_special
:
if
entry
.
is_special
:
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
(
"static PyMethodDef %s = "
%
self
.
entry
.
pymethdef_cname
)
code
.
put_pymethoddef
(
self
.
entry
,
";"
,
allow_skip
=
False
)
entry
.
pymethdef_cname
)
code
.
put_pymethoddef
(
self
.
target
.
entry
,
";"
,
allow_skip
=
False
)
code
.
putln
(
"%s {"
%
header
)
def
generate_argument_declarations
(
self
,
env
,
code
):
for
arg
in
self
.
args
:
if
arg
.
is_generic
:
# or arg.needs_conversion:
if
arg
.
is_generic
:
if
arg
.
needs_conversion
:
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
)
for
entry
in
env
.
var_entries
:
if
entry
.
is_arg
:
code
.
put_var_declaration
(
entry
)
def
generate_keyword_list
(
self
,
code
):
if
self
.
signature_has_generic_args
()
and
\
...
...
@@ -3159,7 +3314,7 @@ class DefNode(FuncDefNode):
def
generate_argument_parsing_code
(
self
,
env
,
code
):
# Generate fast equivalent of PyArg_ParseTuple call for
# 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
code
.
putln
(
"%s = %s;"
%
(
Naming
.
self_cname
,
Naming
.
self_cname
))
...
...
@@ -3175,9 +3330,6 @@ class DefNode(FuncDefNode):
if
not
arg
.
type
.
is_pyobject
:
if
not
arg
.
type
.
create_from_py_utility_code
(
env
):
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
has_star_or_kw_args
:
...
...
@@ -3220,41 +3372,17 @@ class DefNode(FuncDefNode):
code
.
put_var_xdecref_clear
(
self
.
starstar_arg
.
entry
)
else
:
code
.
put_var_decref_clear
(
self
.
starstar_arg
.
entry
)
code
.
put_add_traceback
(
self
.
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_add_traceback
(
self
.
target
.
entry
.
qualified_name
)
code
.
put_finish_refcount_context
()
code
.
putln
(
"return %s;"
%
self
.
error_value
())
if
code
.
label_used
(
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
):
if
arg
.
type
.
is_pyobject
:
if
arg
.
is_generic
:
item
=
PyrexTypes
.
typecast
(
arg
.
type
,
PyrexTypes
.
py_object_type
,
item
)
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
))
else
:
func
=
arg
.
type
.
from_py_function
...
...
@@ -3509,8 +3637,6 @@ class DefNode(FuncDefNode):
code
.
putln
(
"if (unlikely(!%s)) {"
%
self
.
star_arg
.
entry
.
cname
)
if
self
.
starstar_arg
:
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
.
putln
(
'return %s;'
%
self
.
error_value
())
code
.
putln
(
'}'
)
...
...
@@ -3527,10 +3653,10 @@ class DefNode(FuncDefNode):
code
.
putln
(
"PyObject* values[%d] = {%s};"
%
(
max_args
,
','
.
join
(
'0'
*
max_args
)))
if
self
.
defaults_struct
:
if
self
.
target
.
defaults_struct
:
code
.
putln
(
'%s *%s = __Pyx_CyFunction_Defaults(%s, %s);'
%
(
self
.
defaults_struct
,
Naming
.
dynamic_args_cname
,
self
.
defaults_struct
,
Naming
.
self_cname
))
self
.
target
.
defaults_struct
,
Naming
.
dynamic_args_cname
,
self
.
target
.
defaults_struct
,
Naming
.
self_cname
))
# assign borrowed Python default values to the values array,
# so that they can be overwritten by received arguments below
...
...
@@ -3697,10 +3823,6 @@ class DefNode(FuncDefNode):
for
arg
in
self
.
args
:
if
arg
.
needs_conversion
:
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
):
# Generate conversion code for one argument.
...
...
@@ -3768,10 +3890,7 @@ class DefNode(FuncDefNode):
self
.
generate_arg_none_check
(
arg
,
code
)
def
error_value
(
self
):
return
self
.
entry
.
signature
.
error_value
def
caller_will_check_exceptions
(
self
):
return
1
return
self
.
signature
.
error_value
class
GeneratorDefNode
(
DefNode
):
...
...
@@ -7837,6 +7956,8 @@ class CnameDecoratorNode(StatNode):
e
.
cname
=
self
.
cname
e
.
func_cname
=
self
.
cname
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
:
e
.
cname
=
e
.
type
.
cname
=
self
.
cname
else
:
...
...
@@ -7853,12 +7974,16 @@ class CnameDecoratorNode(StatNode):
for
name
,
entry
in
scope
.
entries
.
iteritems
():
if
entry
.
func_cname
:
cname
=
entry
.
cname
if
'.'
in
cname
:
# remove __pyx_base from func_cname
cname
=
cname
.
split
(
'.'
)[
-
1
]
entry
.
func_cname
=
'%s_%s'
%
(
self
.
cname
,
cname
)
entry
.
func_cname
=
self
.
mangle
(
entry
.
cname
)
if
entry
.
pyfunc_cname
:
old
=
entry
.
pyfunc_cname
entry
.
pyfunc_cname
=
self
.
mangle
(
entry
.
pyfunc_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
):
self
.
node
.
analyse_expressions
(
env
)
...
...
Cython/Compiler/Optimize.py
View file @
b22011f8
...
...
@@ -1643,6 +1643,28 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
return
node
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
):
"""Optimize some common methods calls and instantiation patterns
...
...
Cython/Compiler/Options.py
View file @
b22011f8
...
...
@@ -106,6 +106,9 @@ directive_defaults = {
'warn.unused_arg'
:
False
,
'warn.unused_result'
:
False
,
# optimizations
'optimize.inline_defnode_calls'
:
False
,
# remove unreachable code
'remove_unreachable'
:
True
,
...
...
Cython/Compiler/ParseTreeTransforms.py
View file @
b22011f8
...
...
@@ -1493,6 +1493,9 @@ if VALUE is not None:
if
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
:
node
.
body
.
analyse_declarations
(
lenv
)
...
...
@@ -1514,6 +1517,54 @@ if VALUE is not None:
self
.
seen_vars_stack
.
pop
()
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
):
env
=
self
.
env_stack
[
-
1
]
node
.
analyse_declarations
(
env
)
...
...
@@ -1645,11 +1696,13 @@ if VALUE is not None:
return
None
def
visit_CnameDecoratorNode
(
self
,
node
):
self
.
visitchildren
(
node
)
if
not
node
.
node
:
child_node
=
self
.
visit
(
node
.
node
)
if
not
child_node
:
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
def
create_Property
(
self
,
entry
):
...
...
@@ -2065,9 +2118,6 @@ class CreateClosureClasses(CythonTransform):
return
from_closure
,
in_closure
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
if
node
.
is_generator
:
for
entry
in
node
.
local_scope
.
entries
.
values
():
...
...
@@ -2160,6 +2210,10 @@ class CreateClosureClasses(CythonTransform):
self
.
path
.
pop
()
return
node
def
visit_GeneratorBodyDefNode
(
self
,
node
):
self
.
visitchildren
(
node
)
return
node
def
visit_CFuncDefNode
(
self
,
node
):
self
.
visitchildren
(
node
)
return
node
...
...
Cython/Compiler/Pipeline.py
View file @
b22011f8
...
...
@@ -137,6 +137,7 @@ def create_pipeline(context, mode, exclude_classes=()):
from
AutoDocTransforms
import
EmbedSignature
from
Optimize
import
FlattenInListTransform
,
SwitchTransform
,
IterationTransform
from
Optimize
import
EarlyReplaceBuiltinCalls
,
OptimizeBuiltinCalls
from
Optimize
import
InlineDefNodeCalls
from
Optimize
import
ConstantFolding
,
FinalOptimizePhase
from
Optimize
import
DropRefcountingTransform
from
Buffer
import
IntroduceBufferAuxiliaryVars
...
...
@@ -185,6 +186,7 @@ def create_pipeline(context, mode, exclude_classes=()):
MarkOverflowingArithmetic
(
context
),
IntroduceBufferAuxiliaryVars
(
context
),
_check_c_declarations
,
InlineDefNodeCalls
(
context
),
AnalyseExpressionsTransform
(
context
),
FindInvalidUseOfFusedTypes
(
context
),
CreateClosureClasses
(
context
),
## After all lookups and type inference
...
...
Cython/Compiler/Symtab.py
View file @
b22011f8
...
...
@@ -156,6 +156,7 @@ class Entry(object):
from_closure = 0
is_declared_generic = 0
is_readonly = 0
pyfunc_cname = None
func_cname = None
func_modifiers = []
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():
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
)
@
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
():
"""
>>> 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