Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Xavier Thompson
cython
Commits
5aef3d90
Commit
5aef3d90
authored
Aug 06, 2020
by
Xavier Thompson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor the whole cypclass locking framework into a single Visitor
parent
18dee5dc
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
394 additions
and
433 deletions
+394
-433
Cython/Compiler/CypclassTransforms.py
Cython/Compiler/CypclassTransforms.py
+348
-1
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+35
-266
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+8
-117
Cython/Compiler/Pipeline.py
Cython/Compiler/Pipeline.py
+2
-1
Cython/Compiler/Symtab.py
Cython/Compiler/Symtab.py
+1
-48
No files found.
Cython/Compiler/CypclassTransforms.py
View file @
5aef3d90
...
...
@@ -6,9 +6,11 @@
from
__future__
import
absolute_import
import
cython
cython
.
declare
(
Naming
=
object
,
PyrexTypes
=
object
,
EncodedString
=
object
)
cython
.
declare
(
Naming
=
object
,
PyrexTypes
=
object
,
EncodedString
=
object
,
error
=
object
)
from
collections
import
defaultdict
from
contextlib
import
ExitStack
from
itertools
import
chain
from
.
import
Naming
from
.
import
Nodes
...
...
@@ -19,6 +21,7 @@ from . import TreeFragment
from
.StringEncoding
import
EncodedString
from
.ParseTreeTransforms
import
NormalizeTree
,
InterpretCompilerDirectives
,
DecoratorTransform
,
AnalyseDeclarationsTransform
from
.Errors
import
error
#
# Visitor for wrapper cclass injection
...
...
@@ -444,3 +447,347 @@ def NAME(ARGDECLS):
return
method_wrapper
class
CypclassLockTransform
(
Visitor
.
EnvTransform
):
class
StackLock
:
def
__init__
(
self
,
transform
,
obj_entry
,
state
):
self
.
transform
=
transform
self
.
state
=
state
self
.
entry
=
obj_entry
def
__enter__
(
self
):
state
=
self
.
state
entry
=
self
.
entry
self
.
old_rlocked
=
self
.
transform
.
rlocked
[
entry
]
self
.
old_wlocked
=
self
.
transform
.
wlocked
[
entry
]
if
state
==
'rlocked'
:
self
.
transform
.
rlocked
[
entry
]
+=
1
elif
state
==
'wlocked'
:
self
.
transform
.
wlocked
[
entry
]
+=
1
elif
state
==
'unlocked'
:
if
self
.
rlocked
>
0
:
self
.
transform
.
rlocked
[
entry
]
-=
1
elif
self
.
wlocked
>
0
:
self
.
transform
.
wlocked
[
entry
]
-=
1
def
__exit__
(
self
,
*
args
):
entry
=
self
.
entry
self
.
transform
.
rlocked
[
entry
]
=
self
.
old_rlocked
self
.
transform
.
wlocked
[
entry
]
=
self
.
old_wlocked
def
stacklock
(
self
,
obj_entry
,
state
):
return
self
.
StackLock
(
self
,
obj_entry
,
state
)
class
AccessContext
:
def
__init__
(
self
,
collector
,
reading
=
False
,
writing
=
False
,
deleting
=
False
):
self
.
collector
=
collector
self
.
reading
=
reading
self
.
writing
=
writing
self
.
deleting
=
deleting
def
__enter__
(
self
):
self
.
reading
,
self
.
collector
.
reading
=
self
.
collector
.
reading
,
self
.
reading
self
.
writing
,
self
.
collector
.
writing
=
self
.
collector
.
writing
,
self
.
writing
self
.
deleting
,
self
.
collector
.
deleting
=
self
.
collector
.
deleting
,
self
.
deleting
def
__exit__
(
self
,
*
args
):
self
.
collector
.
reading
=
self
.
reading
self
.
collector
.
writing
=
self
.
writing
self
.
collector
.
deleting
=
self
.
deleting
def
accesscontext
(
self
,
reading
=
False
,
writing
=
False
,
deleting
=
False
):
return
self
.
AccessContext
(
self
,
reading
=
reading
,
writing
=
writing
,
deleting
=
deleting
)
def
__call__
(
self
,
root
):
self
.
rlocked
=
defaultdict
(
int
)
self
.
wlocked
=
defaultdict
(
int
)
self
.
reading
=
False
self
.
writing
=
False
self
.
deleting
=
False
return
super
(
CypclassLockTransform
,
self
).
__call__
(
root
)
def
reference_identifier
(
self
,
node
):
while
isinstance
(
node
,
ExprNodes
.
CoerceToTempNode
):
# works for CoerceToLockedTempNode too
node
=
node
.
arg
if
node
.
is_name
:
return
node
.
entry
return
None
def
id_to_name
(
self
,
id
):
return
id
.
name
def
lockcheck_on_context
(
self
,
node
):
if
self
.
writing
or
self
.
deleting
:
return
self
.
lockcheck_written
(
node
)
elif
self
.
reading
:
return
self
.
lockcheck_read
(
node
)
return
node
def
lockcheck_read
(
self
,
read_node
):
lock_mode
=
read_node
.
type
.
lock_mode
if
lock_mode
==
"nolock"
:
return
read_node
ref_id
=
self
.
reference_identifier
(
read_node
)
if
ref_id
:
if
not
(
self
.
rlocked
[
ref_id
]
>
0
or
self
.
wlocked
[
ref_id
]
>
0
):
if
lock_mode
==
"checklock"
:
error
(
read_node
.
pos
,
(
"Reference '%s' is not correctly locked in this expression "
"(read lock required)"
)
%
self
.
id_to_name
(
ref_id
)
)
elif
lock_mode
==
"autolock"
:
# for now, lock a temporary for each expression
return
ExprNodes
.
CoerceToLockedTempNode
(
read_node
,
self
.
current_env
(),
rlock_only
=
True
)
else
:
if
lock_mode
==
"checklock"
:
error
(
read_node
.
pos
,
"This expression is not correctly locked (read lock required)"
)
elif
lock_mode
==
"autolock"
:
if
not
isinstance
(
read_node
,
ExprNodes
.
CoerceToLockedTempNode
):
return
ExprNodes
.
CoerceToLockedTempNode
(
read_node
,
self
.
current_env
(),
rlock_only
=
True
)
return
read_node
def
lockcheck_written
(
self
,
written_node
):
lock_mode
=
written_node
.
type
.
lock_mode
if
lock_mode
==
"nolock"
:
return
written_node
ref_id
=
self
.
reference_identifier
(
written_node
)
if
ref_id
:
if
not
self
.
wlocked
[
ref_id
]
>
0
:
if
lock_mode
==
"checklock"
:
error
(
written_node
.
pos
,
(
"Reference '%s' is not correctly locked in this expression "
"(write lock required)"
)
%
self
.
id_to_name
(
ref_id
)
)
elif
lock_mode
==
"autolock"
:
# for now, lock a temporary for each expression
return
ExprNodes
.
CoerceToLockedTempNode
(
written_node
,
self
.
current_env
(),
rlock_only
=
False
)
else
:
if
lock_mode
==
"checklock"
:
error
(
written_node
.
pos
,
"This expression is not correctly locked (write lock required)"
)
elif
lock_mode
==
"autolock"
:
if
isinstance
(
written_node
,
ExprNodes
.
CoerceToLockedTempNode
):
written_node
.
rlock_only
=
False
else
:
return
ExprNodes
.
CoerceToLockedTempNode
(
written_node
,
self
.
current_env
())
return
written_node
def
lockcheck_written_or_read
(
self
,
node
,
reading
=
False
):
if
reading
:
return
self
.
lockcheck_read
(
node
)
else
:
return
self
.
lockcheck_written
(
node
)
return
node
def
lockcheck_if_subscript_rhs
(
self
,
lhs
,
rhs
):
if
lhs
.
is_subscript
and
lhs
.
base
.
type
.
is_cyp_class
:
setitem
=
lhs
.
base
.
type
.
scope
.
lookup
(
"__setitem__"
)
if
setitem
and
len
(
setitem
.
type
.
args
)
==
2
:
arg_type
=
setitem
.
type
.
args
[
1
].
type
if
arg_type
.
is_cyp_class
:
return
self
.
lockcheck_written_or_read
(
rhs
,
reading
=
arg_type
.
is_const
)
# else: should have caused a previous error
return
rhs
def
visit_CFuncDefNode
(
self
,
node
):
cyp_class_args
=
(
e
for
e
in
node
.
local_scope
.
arg_entries
if
e
.
type
.
is_cyp_class
)
with
ExitStack
()
as
locked_args_stack
:
for
arg
in
cyp_class_args
:
is_rlocked
=
arg
.
type
.
is_const
or
arg
.
is_self_arg
and
node
.
entry
.
type
.
is_const_method
arg_id
=
arg
locked_args_stack
.
enter_context
(
self
.
stacklock
(
arg_id
,
"rlocked"
if
is_rlocked
else
"wlocked"
))
self
.
visit
(
node
.
body
)
return
node
def
visit_LockCypclassNode
(
self
,
node
):
obj_ref_id
=
self
.
reference_identifier
(
node
.
obj
)
if
not
obj_ref_id
:
error
(
node
.
obj
.
pos
,
"Locking an unnamed reference"
)
return
node
if
not
node
.
obj
.
type
.
is_cyp_class
:
error
(
node
.
obj
.
pos
,
"Locking non-cypclass reference"
)
return
node
with
self
.
stacklock
(
obj_ref_id
,
node
.
state
):
self
.
visit
(
node
.
body
)
return
node
def
visit_Node
(
self
,
node
):
with
self
.
accesscontext
(
reading
=
True
):
self
.
visitchildren
(
node
)
return
node
def
visit_DelStatNode
(
self
,
node
):
for
arg
in
node
.
args
:
arg_ref_id
=
self
.
reference_identifier
(
arg
)
if
self
.
rlocked
[
arg_ref_id
]
>
0
or
self
.
wlocked
[
arg_ref_id
]
>
0
:
error
(
arg
.
pos
,
"Deleting a locked cypclass reference"
)
return
node
with
self
.
accesscontext
(
deleting
=
True
):
self
.
visitchildren
(
node
)
return
node
def
visit_SingleAssignmentNode
(
self
,
node
):
lhs_ref_id
=
self
.
reference_identifier
(
node
.
lhs
)
if
self
.
rlocked
[
lhs_ref_id
]
>
0
or
self
.
wlocked
[
lhs_ref_id
]
>
0
:
error
(
node
.
lhs
.
pos
,
"Assigning to a locked cypclass reference"
)
return
node
node
.
rhs
=
self
.
lockcheck_if_subscript_rhs
(
node
.
lhs
,
node
.
rhs
)
with
self
.
accesscontext
(
writing
=
True
):
self
.
visit
(
node
.
lhs
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visit
(
node
.
rhs
)
return
node
def
visit_CascadedAssignmentNode
(
self
,
node
):
for
lhs
in
node
.
lhs_list
:
lhs_ref_id
=
self
.
reference_identifier
(
lhs
)
if
self
.
rlocked
[
lhs_ref_id
]
>
0
or
self
.
wlocked
[
lhs_ref_id
]
>
0
:
error
(
lhs
.
pos
,
"Assigning to a locked cypclass reference"
)
return
node
for
lhs
in
node
.
lhs_list
:
node
.
rhs
=
self
.
lockcheck_if_subscript_rhs
(
lhs
,
node
.
rhs
)
with
self
.
accesscontext
(
writing
=
True
):
for
lhs
in
node
.
lhs_list
:
self
.
visit
(
lhs
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visit
(
node
.
rhs
)
return
node
def
visit_WithTargetAssignmentStatNode
(
self
,
node
):
target_id
=
self
.
reference_identifier
(
node
.
lhs
)
if
self
.
rlocked
[
target_id
]
>
0
or
self
.
wlocked
[
target_id
]
>
0
:
error
(
node
.
lhs
.
pos
,
"With expression target is a locked cypclass reference"
)
return
node
node
.
rhs
=
self
.
lockcheck_if_subscript_rhs
(
node
.
lhs
,
node
.
rhs
)
with
self
.
accesscontext
(
writing
=
True
):
self
.
visit
(
node
.
lhs
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visit
(
node
.
rhs
)
return
node
def
visit__ForInStatNode
(
self
,
node
):
target_id
=
self
.
reference_identifier
(
node
.
target
)
if
self
.
rlocked
[
target_id
]
>
0
or
self
.
wlocked
[
target_id
]
>
0
:
error
(
node
.
target
.
pos
,
"For-Loop target is a locked cypclass reference"
)
return
node
node
.
item
=
self
.
lockcheck_if_subscript_rhs
(
node
.
target
,
node
.
item
)
with
self
.
accesscontext
(
writing
=
True
):
self
.
visit
(
node
.
target
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visit
(
node
.
item
)
self
.
visit
(
node
.
body
)
self
.
visit
(
node
.
iterator
)
if
node
.
else_clause
:
self
.
visit
(
node
.
else_clause
)
return
node
def
visit_ExceptClauseNode
(
self
,
node
):
if
not
node
.
target
:
self
.
visitchildren
(
node
)
else
:
target_id
=
self
.
reference_identifier
(
node
.
target
)
if
self
.
rlocked
[
target_id
]
>
0
or
self
.
wlocked
[
target_id
]
>
0
:
error
(
node
.
target
.
pos
,
"Except clause target is a locked cypclass reference"
)
return
node
with
self
.
accesscontext
(
writing
=
True
):
self
.
visit
(
node
.
target
)
for
p
in
node
.
pattern
:
self
.
visit
(
p
)
self
.
visit
(
node
.
body
)
return
node
def
visit_AttributeNode
(
self
,
node
):
if
node
.
obj
.
type
and
node
.
obj
.
type
.
is_cyp_class
:
if
node
.
is_called
:
if
not
node
.
type
.
is_static_method
:
node
.
obj
=
self
.
lockcheck_written_or_read
(
node
.
obj
,
reading
=
node
.
type
.
is_const_method
)
else
:
node
.
obj
=
self
.
lockcheck_on_context
(
node
.
obj
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visitchildren
(
node
)
return
node
def
visit_SimpleCallNode
(
self
,
node
):
for
i
,
arg
in
enumerate
(
node
.
args
or
()):
# provide an empty tuple fallback in case node.args is None
if
arg
.
type
.
is_cyp_class
:
node
.
args
[
i
]
=
self
.
lockcheck_written_or_read
(
arg
,
reading
=
arg
.
type
.
is_const
)
# TODO: lock callable objects
with
self
.
accesscontext
(
reading
=
True
):
self
.
visitchildren
(
node
)
return
node
def
visit_IndexNode
(
self
,
node
):
if
node
.
base
.
type
.
is_cyp_class
:
func_entry
=
None
if
self
.
deleting
:
func_entry
=
node
.
base
.
type
.
scope
.
lookup
(
"__delitem__"
)
elif
self
.
writing
:
func_entry
=
node
.
base
.
type
.
scope
.
lookup
(
"__setitem__"
)
elif
self
.
reading
:
func_entry
=
node
.
base
.
type
.
scope
.
lookup
(
"__getitem__"
)
if
func_entry
:
func_type
=
func_entry
.
type
node
.
base
=
self
.
lockcheck_written_or_read
(
node
.
base
,
reading
=
func_type
.
is_const_method
)
if
len
(
func_type
.
args
):
node
.
index
=
self
.
lockcheck_written_or_read
(
node
.
index
,
reading
=
func_type
.
args
[
0
].
type
.
is_const
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visitchildren
(
node
)
return
node
def
_visit_binop
(
self
,
node
,
func_type
):
if
func_type
is
not
None
:
if
node
.
operand1
.
type
.
is_cyp_class
and
len
(
func_type
.
args
)
==
1
:
node
.
operand1
=
self
.
lockcheck_written_or_read
(
node
.
operand1
,
reading
=
func_type
.
is_const_method
)
arg_type
=
func_type
.
args
[
0
].
type
if
arg_type
.
is_cyp_class
:
node
.
operand2
=
self
.
lockcheck_written_or_read
(
node
.
operand2
,
reading
=
arg_type
.
is_const
)
elif
len
(
func_type
.
args
)
==
2
:
arg1_type
=
func_type
.
args
[
0
].
type
if
arg1_type
.
is_cyp_class
:
node
.
operand1
=
self
.
lockcheck_written_or_read
(
node
.
operand1
,
reading
=
arg1_type
.
is_const
)
arg2_type
=
func_type
.
args
[
1
].
type
if
arg2_type
.
is_cyp_class
:
node
.
operand2
=
self
.
lockcheck_written_or_read
(
node
.
operand2
,
reading
=
arg2_type
.
is_const
)
def
visit_BinopNode
(
self
,
node
):
func_type
=
node
.
op_func_type
self
.
_visit_binop
(
node
,
func_type
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visitchildren
(
node
)
return
node
def
visit_PrimaryCmpNode
(
self
,
node
):
func_type
=
node
.
cmp_func_type
self
.
_visit_binop
(
node
,
func_type
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visitchildren
(
node
)
return
node
def
visit_InPlaceAssignmentNode
(
self
,
node
):
# operator = "operator%s="% node.operator
# if node.lhs.type.is_cyp_class:
# TODO: get operator function type and treat it like a binop with lhs and rhs
with
self
.
accesscontext
(
reading
=
True
,
writing
=
True
):
self
.
visit
(
node
.
lhs
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visit
(
node
.
rhs
)
return
node
def
_visit_unop
(
self
,
node
,
func_type
):
if
func_type
is
not
None
:
if
node
.
operand
.
type
.
is_cyp_class
and
len
(
func_type
.
args
)
==
0
:
node
.
operand
=
self
.
lockcheck_written_or_read
(
node
.
operand
,
reading
=
func_type
.
is_const_method
)
def
visit_UnopNode
(
self
,
node
):
func_type
=
node
.
op_func_type
self
.
_visit_unop
(
node
,
func_type
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visitchildren
(
node
)
return
node
def
visit_TypecastNode
(
self
,
node
):
func_type
=
node
.
op_func_type
self
.
_visit_unop
(
node
,
func_type
)
with
self
.
accesscontext
(
reading
=
True
):
self
.
visitchildren
(
node
)
return
node
Cython/Compiler/ExprNodes.py
View file @
5aef3d90
...
...
@@ -320,7 +320,6 @@ class ExprNode(Node):
use_managed_ref
=
True
# can be set by optimisation transforms
result_is_used
=
True
is_numpy_attribute
=
False
tracked_state
=
None
# The Analyse Expressions phase for expressions is split
# into two sub-phases:
...
...
@@ -724,89 +723,6 @@ class ExprNode(Node):
def
addr_not_const
(
self
):
error
(
self
.
pos
,
"Address is not constant"
)
def
set_autorlock
(
self
,
env
):
self
.
tracked_state
.
is_rlocked
=
True
self
.
tracked_state
.
needs_rlock
=
True
def
set_autowlock
(
self
,
env
):
self
.
tracked_state
.
is_wlocked
=
True
self
.
tracked_state
.
needs_wlock
=
True
def
needs_rlock
(
self
):
if
self
.
tracked_state
is
None
:
return
False
return
self
.
tracked_state
.
needs_rlock
def
needs_wlock
(
self
):
if
self
.
tracked_state
is
None
:
return
False
return
self
.
tracked_state
.
needs_wlock
def
is_autolock
(
self
):
return
self
.
type
is
not
None
and
self
.
type
.
is_cyp_class
and
self
.
type
.
lock_mode
==
"autolock"
def
is_checklock
(
self
):
return
self
.
type
is
not
None
and
self
.
type
.
is_cyp_class
and
self
.
type
.
lock_mode
==
"checklock"
def
get_tracked_state
(
self
,
env
):
if
not
hasattr
(
self
,
'entry'
)
or
not
self
.
entry
or
not
self
.
entry
.
type
.
is_cyp_class
:
return
self
.
tracked_state
=
env
.
lookup_tracked
(
self
.
entry
)
if
self
.
tracked_state
is
None
:
self
.
tracked_state
=
env
.
declare_tracked
(
self
.
entry
)
if
self
.
is_autolock
()
and
self
.
entry
.
is_variable
:
env
.
declare_autolocked
(
self
)
def
is_rhs_locked
(
self
,
env
):
if
not
hasattr
(
self
,
'entry'
)
or
self
.
entry
.
type
is
None
or
not
self
.
entry
.
type
.
is_cyp_class
:
# These nodes couldn't be tracked (because it is for example a constant),
# so we let them pass silently
return
True
return
self
.
tracked_state
.
is_rlocked
or
self
.
tracked_state
.
is_wlocked
def
is_lhs_locked
(
self
,
env
):
if
not
hasattr
(
self
,
'entry'
)
or
self
.
entry
.
type
is
None
or
not
self
.
entry
.
type
.
is_cyp_class
:
# These nodes couldn't be tracked (because it is for example a constant),
# so we let them pass silently
return
True
return
self
.
tracked_state
.
is_wlocked
def
ensure_subexpr_rhs_locked
(
self
,
env
):
for
node
in
self
.
subexpr_nodes
():
node
.
ensure_rhs_locked
(
env
)
def
ensure_subexpr_lhs_locked
(
self
,
env
):
for
node
in
self
.
subexpr_nodes
():
node
.
ensure_lhs_locked
(
env
)
def
ensure_rhs_locked
(
self
,
env
,
is_dereferenced
=
False
):
self
.
ensure_subexpr_rhs_locked
(
env
)
if
not
self
.
tracked_state
:
self
.
get_tracked_state
(
env
)
if
is_dereferenced
and
self
.
tracked_state
:
if
not
self
.
is_rhs_locked
(
env
):
if
self
.
is_checklock
():
error
(
self
.
pos
,
"This expression is not correctly locked (read lock needed)"
)
elif
self
.
is_autolock
():
self
.
set_autorlock
(
env
)
def
ensure_lhs_locked
(
self
,
env
,
is_dereferenced
=
False
,
is_top_lhs
=
False
):
if
not
is_dereferenced
:
self
.
ensure_subexpr_lhs_locked
(
env
)
else
:
self
.
ensure_subexpr_rhs_locked
(
env
)
if
not
self
.
tracked_state
:
self
.
get_tracked_state
(
env
)
if
self
.
is_autolock
()
and
is_top_lhs
:
#env.declare_autolocked(self)
self
.
tracked_as_lhs
=
True
if
is_dereferenced
and
self
.
tracked_state
:
if
not
self
.
is_lhs_locked
(
env
):
if
self
.
is_checklock
():
error
(
self
.
pos
,
"This expression is not correctly locked (write lock needed)"
)
elif
self
.
is_autolock
():
self
.
set_autowlock
(
env
)
# ----------------- Result Allocation -----------------
def
result_in_temp
(
self
):
...
...
@@ -2438,7 +2354,6 @@ class NameNode(AtomicExprNode):
exception_check
=
None
,
exception_value
=
None
):
#print "NameNode.generate_assignment_code:", self.name ###
entry
=
self
.
entry
tracked_state
=
self
.
tracked_state
if
entry
is
None
:
return
# There was an error earlier
...
...
@@ -2446,9 +2361,6 @@ class NameNode(AtomicExprNode):
and
not
self
.
lhs_of_first_assignment
and
not
rhs
.
in_module_scope
):
error
(
self
.
pos
,
"Literal list must be assigned to pointer at time of declaration"
)
if
self
.
is_autolock
()
and
tracked_state
and
(
tracked_state
.
needs_wlock
or
tracked_state
.
needs_rlock
):
code
.
putln
(
"Cy_UNLOCK(%s);"
%
self
.
result
())
# is_pyglobal seems to be True for module level-globals only.
# We use this to access class->tp_dict if necessary.
if
entry
.
is_pyglobal
:
...
...
@@ -2546,11 +2458,6 @@ class NameNode(AtomicExprNode):
code
.
putln
(
'new (&%s) decltype(%s){%s};'
%
(
self
.
result
(),
self
.
result
(),
result
))
elif
result
!=
self
.
result
():
code
.
putln
(
'%s = %s;'
%
(
self
.
result
(),
result
))
if
self
.
is_autolock
():
if
tracked_state
.
needs_wlock
:
code
.
putln
(
"Cy_WLOCK(%s);"
%
self
.
result
())
elif
tracked_state
.
needs_rlock
:
code
.
putln
(
"Cy_RLOCK(%s);"
%
self
.
result
())
if
debug_disposal_code
:
print
(
"NameNode.generate_assignment_code:"
)
print
(
"...generating post-assignment code for %s"
%
rhs
)
...
...
@@ -3981,16 +3888,6 @@ class IndexNode(_IndexingBaseNode):
error
(
self
.
pos
,
"Invalid index type '%s'"
%
self
.
index
.
type
)
return
self
def
ensure_base_and_index_locked
(
self
,
env
,
func_type
):
if
func_type
.
is_const_method
:
self
.
base
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
base
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
if
func_type
.
args
[
0
].
type
.
is_const
:
self
.
index
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
index
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
def
analyse_as_cpp
(
self
,
env
,
setting
):
base_type
=
self
.
base
.
type
function
=
env
.
lookup_operator
(
"[]"
,
[
self
.
base
,
self
.
index
])
...
...
@@ -4009,7 +3906,6 @@ class IndexNode(_IndexingBaseNode):
self
.
is_temp
=
True
if
self
.
exception_value
is
None
:
env
.
use_utility_code
(
UtilityCode
.
load_cached
(
"CppExceptionConversion"
,
"CppSupport.cpp"
))
self
.
ensure_base_and_index_locked
(
env
,
func_type
)
self
.
index
=
self
.
index
.
coerce_to
(
func_type
.
args
[
0
].
type
,
env
)
self
.
type
=
func_type
.
return_type
if
setting
and
not
func_type
.
return_type
.
is_reference
:
...
...
@@ -4065,7 +3961,6 @@ class IndexNode(_IndexingBaseNode):
self
.
result_code
=
"<error>"
return
self
self
.
type
=
setitem_type
.
args
[
1
].
type
self
.
ensure_base_and_index_locked
(
env
,
func_type
)
return
self
def
analyse_as_c_function
(
self
,
env
):
...
...
@@ -5164,13 +5059,6 @@ class SliceIndexNode(ExprNode):
index
=
not_a_constant
return
self
.
base
.
inferable_item_node
(
index
)
def
ensure_subexpr_lhs_locked
(
self
,
env
):
self
.
base
.
ensure_lhs_locked
(
env
)
if
self
.
start
:
self
.
start
.
ensure_rhs_locked
(
env
)
elif
self
.
stop
:
self
.
stop
.
ensure_rhs_locked
(
env
)
def
may_be_none
(
self
):
base_type
=
self
.
base
.
type
if
base_type
:
...
...
@@ -5926,9 +5814,6 @@ class SimpleCallNode(CallNode):
# analysed bool used internally
# overflowcheck bool used internally
# explicit_cpp_self bool used internally
# rlocked bool used internally
# wlocked bool used internally
# tracked_state bool used internally
# needs_deref bool used internally
subexprs
=
[
'self'
,
'coerced_self'
,
'function'
,
'args'
,
'arg_tuple'
]
...
...
@@ -5942,9 +5827,6 @@ class SimpleCallNode(CallNode):
analysed
=
False
overflowcheck
=
False
explicit_cpp_self
=
None
rlocked
=
False
wlocked
=
False
tracked_state
=
True
# Something random, anything that is not None
needs_deref
=
False
def
compile_time_value
(
self
,
denv
):
...
...
@@ -6276,58 +6158,6 @@ class SimpleCallNode(CallNode):
self
.
overflowcheck
=
env
.
directives
[
'overflowcheck'
]
def
ensure_subexpr_rhs_locked
(
self
,
env
):
func_type
=
self
.
function_type
()
if
func_type
.
is_pyobject
:
self
.
arg_tuple
.
ensure_rhs_locked
(
env
)
elif
func_type
.
is_cfunction
:
max_nargs
=
len
(
func_type
.
args
)
actual_nargs
=
len
(
self
.
args
)
# Check for args locks: read-lock for const args, write-locks for other
for
i
in
range
(
min
(
max_nargs
,
actual_nargs
)):
formal_arg
=
func_type
.
args
[
i
]
actual_arg
=
self
.
args
[
i
]
deref_flag
=
formal_arg
.
type
.
is_cyp_class
wlock_flag
=
deref_flag
and
not
formal_arg
.
type
.
is_const
if
wlock_flag
:
actual_arg
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
actual_arg
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
deref_flag
)
# XXX - Should we do something in a pyfunc case ?
if
func_type
.
is_static_method
:
pass
# no need to lock the object on which a static method is called
elif
func_type
.
is_const_method
:
self
.
function
.
ensure_rhs_locked
(
env
)
else
:
self
.
function
.
ensure_lhs_locked
(
env
)
def
ensure_subexpr_lhs_locked
(
self
,
env
):
# This may be seen a bit weird
# In fact, the only thing that changes between lhs & rhs analysis for function
# calls is that the result should be locked, but the subexpr analysis is
# exactly the same, because the result is not explicitely tied to args
# and base object (in case of a method call).
self
.
ensure_subexpr_rhs_locked
(
env
)
def
is_lhs_locked
(
self
,
env
):
return
self
.
wlocked
def
is_rhs_locked
(
self
,
env
):
return
self
.
rlocked
def
set_autorlock
(
self
,
env
):
self
.
rlocked
=
True
def
set_autowlock
(
self
,
env
):
self
.
wlocked
=
True
def
needs_rlock
(
self
):
return
self
.
rlocked
def
needs_wlock
(
self
):
return
self
.
wlocked
def
calculate_result_code
(
self
):
return
self
.
c_call_code
()
...
...
@@ -6505,10 +6335,6 @@ class SimpleCallNode(CallNode):
else
:
goto_error
=
""
code
.
putln
(
"%s%s; %s"
%
(
lhs
,
rhs
,
goto_error
))
if
self
.
wlocked
:
code
.
putln
(
"Cy_WLOCK(%s);"
%
self
.
result
())
elif
self
.
rlocked
:
code
.
putln
(
"Cy_RLOCK(%s);"
%
self
.
result
())
if
self
.
type
.
is_pyobject
and
self
.
result
():
self
.
generate_gotref
(
code
)
elif
self
.
type
.
is_cyp_class
and
self
.
result
():
...
...
@@ -6516,10 +6342,6 @@ class SimpleCallNode(CallNode):
if
self
.
has_optional_args
:
code
.
funcstate
.
release_temp
(
self
.
opt_arg_struct
)
def
generate_disposal_code
(
self
,
code
):
if
self
.
wlocked
or
self
.
rlocked
:
code
.
putln
(
"Cy_UNLOCK(%s);"
%
self
.
result
())
ExprNode
.
generate_disposal_code
(
self
,
code
)
class
NumPyMethodCallNode
(
ExprNode
):
# Pythran call to a NumPy function or method.
...
...
@@ -7648,22 +7470,6 @@ class AttributeNode(ExprNode):
gil_message
=
"Accessing Python attribute"
def
ensure_subexpr_rhs_locked
(
self
,
env
):
if
not
self
.
entry
:
self
.
obj
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
elif
self
.
entry
.
is_cfunction
:
if
self
.
entry
.
type
.
is_static_method
:
pass
elif
self
.
entry
.
type
.
is_const_method
:
self
.
obj
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
obj
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
obj
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
def
ensure_subexpr_lhs_locked
(
self
,
env
):
self
.
obj
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
def
is_cimported_module_without_shadow
(
self
,
env
):
return
self
.
obj
.
is_cimported_module_without_shadow
(
env
)
...
...
@@ -7806,10 +7612,6 @@ class AttributeNode(ExprNode):
rhs
.
free_temps
(
code
)
else
:
select_code
=
self
.
result
()
# XXX - Greater to have a getter, right ?
tracked_state
=
self
.
tracked_state
if
self
.
is_autolock
()
and
tracked_state
and
(
tracked_state
.
needs_rlock
or
tracked_state
.
needs_wlock
):
code
.
putln
(
"Cy_UNLOCK(%s);"
%
select_code
)
if
self
.
type
.
is_pyobject
and
self
.
use_managed_ref
:
rhs
.
make_owned_reference
(
code
)
rhs
.
generate_giveref
(
code
)
...
...
@@ -7831,11 +7633,6 @@ class AttributeNode(ExprNode):
select_code
,
rhs
.
move_result_rhs_as
(
self
.
ctype
())))
#rhs.result()))
if
self
.
is_autolock
():
if
self
.
needs_wlock
():
code
.
putln
(
"Cy_WLOCK(%s);"
%
select_code
)
elif
self
.
needs_rlock
():
code
.
putln
(
"Cy_RLOCK(%s);"
%
select_code
)
rhs
.
generate_post_assignment_code
(
code
)
rhs
.
free_temps
(
code
)
...
...
@@ -10552,6 +10349,7 @@ compile_time_unary_operators = {
class
UnopNode
(
ExprNode
):
# operator string
# operand ExprNode
# op_func_type CFuncType or None
#
# Processing during analyse_expressions phase:
#
...
...
@@ -10563,6 +10361,7 @@ class UnopNode(ExprNode):
subexprs
=
[
'operand'
]
infix
=
True
op_func_type
=
None
def
calculate_constant_result
(
self
):
func
=
compile_time_unary_operators
[
self
.
operator
]
...
...
@@ -10679,6 +10478,7 @@ class UnopNode(ExprNode):
self
.
type_error
()
return
if
entry
:
self
.
op_func_type
=
entry
.
type
self
.
exception_check
=
entry
.
type
.
exception_check
self
.
exception_value
=
entry
.
type
.
exception_value
if
self
.
exception_check
==
'+'
:
...
...
@@ -10934,6 +10734,7 @@ class TypecastNode(ExprNode):
# declarator CDeclaratorNode
# typecheck boolean
# overloaded boolean
# op_func_type CFuncType or None
#
# If used from a transform, one can if wanted specify the attribute
# "type" directly and leave base_type and declarator to None
...
...
@@ -10941,6 +10742,7 @@ class TypecastNode(ExprNode):
subexprs
=
[
'operand'
]
base_type
=
declarator
=
type
=
None
overloaded
=
False
op_func_type
=
None
def
type_dependencies
(
self
,
env
):
return
()
...
...
@@ -11010,6 +10812,8 @@ class TypecastNode(ExprNode):
operator
=
'operator '
+
self
.
type
.
declaration_code
(
''
)
entry
=
self
.
operand
.
type
.
scope
.
lookup_here
(
operator
)
self
.
overloaded
=
entry
is
not
None
if
entry
:
self
.
op_func_type
=
entry
.
type
if
self
.
type
.
is_cyp_class
:
self
.
is_temp
=
True
if
self
.
type
.
is_ptr
and
self
.
type
.
base_type
.
is_cfunction
and
self
.
type
.
base_type
.
nogil
:
...
...
@@ -11584,6 +11388,7 @@ class BinopNode(ExprNode):
# operator string
# operand1 ExprNode
# operand2 ExprNode
# op_func_type CFuncType or None
#
# Processing during analyse_expressions phase:
#
...
...
@@ -11595,6 +11400,7 @@ class BinopNode(ExprNode):
subexprs
=
[
'operand1'
,
'operand2'
]
inplace
=
False
op_func_type
=
None
def
calculate_constant_result
(
self
):
func
=
compile_time_binary_operators
[
self
.
operator
]
...
...
@@ -11704,25 +11510,10 @@ class BinopNode(ExprNode):
env
.
use_utility_code
(
UtilityCode
.
load_cached
(
"CppExceptionConversion"
,
"CppSupport.cpp"
))
if
func_type
.
is_ptr
:
func_type
=
func_type
.
base_type
self
.
op_func_type
=
func_type
if
len
(
func_type
.
args
)
==
1
:
if
func_type
.
is_const_method
:
self
.
operand1
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
operand1
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
if
func_type
.
args
[
0
].
type
.
is_const
:
self
.
operand2
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
operand2
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
self
.
operand2
=
self
.
operand2
.
coerce_to
(
func_type
.
args
[
0
].
type
,
env
)
else
:
if
func_type
.
args
[
0
].
type
.
is_const
:
self
.
operand1
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
operand1
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
if
func_type
.
args
[
1
].
type
.
is_const
:
self
.
operand2
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
operand2
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
self
.
operand1
=
self
.
operand1
.
coerce_to
(
func_type
.
args
[
0
].
type
,
env
)
self
.
operand2
=
self
.
operand2
.
coerce_to
(
func_type
.
args
[
1
].
type
,
env
)
self
.
type
=
func_type
.
return_type
...
...
@@ -13200,6 +12991,7 @@ class PrimaryCmpNode(ExprNode, CmpNode):
# operand1 ExprNode
# operand2 ExprNode
# cascade CascadedCmpNode
# cmp_func_type CFuncType or None
# We don't use the subexprs mechanism, because
# things here are too complicated for it to handle.
...
...
@@ -13211,6 +13003,7 @@ class PrimaryCmpNode(ExprNode, CmpNode):
cascade
=
None
coerced_operand2
=
None
is_memslice_nonecheck
=
False
cmp_func_type
=
None
def
infer_type
(
self
,
env
):
type1
=
self
.
operand1
.
infer_type
(
env
)
...
...
@@ -13243,12 +13036,6 @@ class PrimaryCmpNode(ExprNode, CmpNode):
operand1
=
self
.
operand1
.
compile_time_value
(
denv
)
return
self
.
cascaded_compile_time_value
(
operand1
,
denv
)
#def check_rhs_locked(self, env):
# self.operand1.check_rhs_locked(env)
# self.operand2.check_rhs_locked(env)
# if self.cascade:
# self.cascade.check_rhs_locked(env)
def
analyse_types
(
self
,
env
):
self
.
operand1
=
self
.
operand1
.
analyse_types
(
env
)
self
.
operand2
=
self
.
operand2
.
analyse_types
(
env
)
...
...
@@ -13390,6 +13177,7 @@ class PrimaryCmpNode(ExprNode, CmpNode):
func_type
=
entry
.
type
if
func_type
.
is_ptr
:
func_type
=
func_type
.
base_type
self
.
cmp_func_type
=
func_type
self
.
exception_check
=
func_type
.
exception_check
self
.
exception_value
=
func_type
.
exception_value
if
self
.
exception_check
==
'+'
:
...
...
@@ -13397,24 +13185,8 @@ class PrimaryCmpNode(ExprNode, CmpNode):
if
self
.
exception_value
is
None
:
env
.
use_utility_code
(
UtilityCode
.
load_cached
(
"CppExceptionConversion"
,
"CppSupport.cpp"
))
if
len
(
func_type
.
args
)
==
1
:
if
func_type
.
is_const_method
:
self
.
operand1
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
operand1
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
if
func_type
.
args
[
0
].
type
.
is_const
:
self
.
operand2
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
operand2
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
self
.
operand2
=
self
.
operand2
.
coerce_to
(
func_type
.
args
[
0
].
type
,
env
)
else
:
if
func_type
.
args
[
0
].
type
.
is_const
:
self
.
operand1
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
operand1
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
if
func_type
.
args
[
1
].
type
.
is_const
:
self
.
operand2
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
operand2
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
self
.
operand1
=
self
.
operand1
.
coerce_to
(
func_type
.
args
[
0
].
type
,
env
)
self
.
operand2
=
self
.
operand2
.
coerce_to
(
func_type
.
args
[
1
].
type
,
env
)
self
.
type
=
func_type
.
return_type
...
...
@@ -13460,12 +13232,6 @@ class PrimaryCmpNode(ExprNode, CmpNode):
else
:
return
self
.
operand1
.
check_const
()
and
self
.
operand2
.
check_const
()
def
ensure_subexpr_rhs_locked
(
self
,
env
):
self
.
operand1
.
ensure_rhs_locked
(
env
)
self
.
operand2
.
ensure_rhs_locked
(
env
)
if
self
.
cascade
:
self
.
cascade
.
ensure_rhs_locked
(
env
)
def
calculate_result_code
(
self
):
operand1
,
operand2
=
self
.
operand1
,
self
.
operand2
if
operand1
.
type
.
is_complex
:
...
...
@@ -13577,16 +13343,6 @@ class CascadedCmpNode(Node, CmpNode):
return
self
.
constant_result
is
not
constant_value_not_set
and
\
self
.
constant_result
is
not
not_a_constant
#def check_rhs_locked(self, env):
# self.operand2.check_rhs_locked(env)
# if self.cascade:
# self.cascade.check_rhs_locked(env)
def
ensure_rhs_locked
(
self
,
env
):
self
.
operand2
.
ensure_rhs_locked
(
env
)
if
self
.
cascade
:
self
.
cascade
.
ensure_rhs_locked
(
env
)
def
analyse_types
(
self
,
env
):
self
.
operand2
=
self
.
operand2
.
analyse_types
(
env
)
if
self
.
cascade
:
...
...
@@ -14190,7 +13946,7 @@ class CoerceToTempNode(CoercionNode):
# to be stored in a temporary. It is only used if the
# argument node's result is not already in a temporary.
def
__init__
(
self
,
arg
,
env
):
def
__init__
(
self
,
arg
,
env
=
None
):
CoercionNode
.
__init__
(
self
,
arg
)
self
.
type
=
self
.
arg
.
type
.
as_argument_type
()
self
.
constant_result
=
self
.
arg
.
constant_result
...
...
@@ -14207,14 +13963,6 @@ class CoerceToTempNode(CoercionNode):
def
may_be_none
(
self
):
return
self
.
arg
.
may_be_none
()
def
ensure_rhs_locked
(
self
,
env
,
is_dereferenced
=
False
):
self
.
arg
.
ensure_rhs_locked
(
env
,
is_dereferenced
)
self
.
tracked_state
=
self
.
arg
.
tracked_state
def
ensure_lhs_locked
(
self
,
env
,
is_dereferenced
=
False
):
self
.
arg
.
ensure_lhs_locked
(
env
,
is_dereferenced
)
self
.
tracked_state
=
self
.
arg
.
tracked_state
def
coerce_to_boolean
(
self
,
env
):
self
.
arg
=
self
.
arg
.
coerce_to_boolean
(
env
)
if
self
.
arg
.
is_simple
():
...
...
@@ -14238,6 +13986,27 @@ class CoerceToTempNode(CoercionNode):
code
.
put_incref_memoryviewslice
(
self
.
result
(),
self
.
type
,
have_gil
=
not
self
.
in_nogil_context
)
class
CoerceToLockedTempNode
(
CoerceToTempNode
):
# rlock_only boolean
def
__init__
(
self
,
arg
,
env
=
None
,
rlock_only
=
False
):
self
.
rlock_only
=
rlock_only
if
isinstance
(
arg
,
CoerceToTempNode
):
arg
=
arg
.
arg
super
(
CoerceToLockedTempNode
,
self
).
__init__
(
arg
,
env
)
def
generate_result_code
(
self
,
code
):
super
(
CoerceToLockedTempNode
,
self
).
generate_result_code
(
code
)
if
self
.
rlock_only
:
code
.
putln
(
"Cy_RLOCK(%s);"
%
self
.
result
())
else
:
code
.
putln
(
"Cy_WLOCK(%s);"
%
self
.
result
())
def
generate_disposal_code
(
self
,
code
):
code
.
putln
(
"Cy_UNLOCK(%s);"
%
self
.
result
())
super
(
CoerceToLockedTempNode
,
self
).
generate_disposal_code
(
code
)
class
ProxyNode
(
CoercionNode
):
"""
A node that should not be replaced by transforms or other means,
...
...
Cython/Compiler/Nodes.py
View file @
5aef3d90
...
...
@@ -2136,7 +2136,7 @@ class FuncDefNode(StatNode, BlockNode):
code
.
put_var_incref_memoryviewslice
(
entry
,
have_gil
=
code
.
funcstate
.
gil_owned
)
# We have to Cy_INCREF the nogil classes (ccdef'ed ones)
elif
entry
.
type
.
is_cyp_class
and
len
(
entry
.
cf_assignments
)
>
1
:
elif
entry
.
type
.
is_cyp_class
and
len
(
entry
.
cf_assignments
)
>
1
and
not
entry
.
is_self_arg
:
code
.
put_cyincref
(
entry
.
cname
)
for
entry
in
lenv
.
var_entries
:
if
entry
.
is_arg
and
entry
.
cf_is_reassigned
and
not
entry
.
in_closure
:
...
...
@@ -2161,15 +2161,6 @@ class FuncDefNode(StatNode, BlockNode):
code
.
put_release_ensured_gil
()
code
.
funcstate
.
gil_owned
=
False
for
node
in
lenv
.
autolocked_nodes
:
if
node
.
entry
.
is_variable
and
not
node
.
entry
.
is_local
and
(
node
.
needs_wlock
()
or
node
.
needs_rlock
()):
node_result
=
node
.
result
()
code
.
putln
(
"if (%s != NULL)"
%
node_result
)
if
node
.
needs_wlock
():
code
.
putln
(
" Cy_WLOCK(%s);"
%
node_result
)
elif
node
.
needs_rlock
():
code
.
putln
(
" Cy_RLOCK(%s);"
%
node_result
)
# -------------------------
# ----- Function body -----
# -------------------------
...
...
@@ -2325,23 +2316,6 @@ class FuncDefNode(StatNode, BlockNode):
align_error_path_gil_to_success_path
()
code
.
put_label
(
code
.
return_from_error_cleanup_label
)
for
node
in
reversed
(
lenv
.
autolocked_nodes
):
# We iterate in the reverse order to properly unlock
# nested locked objects (aka most nested first).
# For example, if we have the following situation:
# obj.sub_obj.attr = some_value
# If obj and sub_obj are both of autolocked types,
# the obj (name)node is declared before the sub_obj (attribute)node.
# If we unlock first obj, another thread could immediately acquire
# a write lock and change where sub_obj points to.
# We would then try to unlock the new sub_obj reference,
# which leads to a dangling lock on the previous reference
# (and attempt to unlock a non-locked ref).
if
node
.
needs_wlock
()
or
node
.
needs_rlock
():
code
.
putln
(
"Cy_UNLOCK(%s);"
%
node
.
result
())
for
entry
in
lenv
.
var_entries
:
if
not
entry
.
used
or
entry
.
in_closure
:
continue
...
...
@@ -2371,7 +2345,7 @@ class FuncDefNode(StatNode, BlockNode):
continue
if
entry
.
type
.
needs_refcounting
:
assure_gil
(
'success'
)
if
entry
.
type
.
is_cyp_class
:
if
entry
.
type
.
is_cyp_class
and
not
entry
.
is_self_arg
:
# We must check for NULL because it is possible to have
# NULL as a valid cypclass (with a typecast)
code
.
put_cyxdecref
(
entry
.
cname
)
...
...
@@ -2741,11 +2715,8 @@ class CFuncDefNode(FuncDefNode):
_cname
=
"this"
entry
=
self
.
local_scope
.
declare
(
_name
,
_cname
,
_type
,
_pos
,
'private'
)
entry
.
is_variable
=
1
# Even if it is checklock it should be OK to mess with self without locking
self_locking_state
=
self
.
local_scope
.
declare_tracked
(
entry
)
self_locking_state
.
is_rlocked
=
self
.
is_const_method
self_locking_state
.
is_wlocked
=
not
self
.
is_const_method
entry
.
is_self_arg
=
1
self
.
local_scope
.
arg_entries
.
append
(
entry
)
def
declare_cpdef_wrapper
(
self
,
env
):
if
self
.
overridable
:
...
...
@@ -5787,9 +5758,6 @@ class ExprStatNode(StatNode):
def
analyse_expressions
(
self
,
env
):
self
.
expr
.
result_is_used
=
False
# hint that .result() may safely be left empty
self
.
expr
=
self
.
expr
.
analyse_expressions
(
env
)
from
.
import
ExprNodes
if
isinstance
(
self
.
expr
,
ExprNodes
.
ExprNode
):
self
.
expr
.
ensure_rhs_locked
(
env
)
# Repeat in case of node replacement.
self
.
expr
.
result_is_used
=
False
# hint that .result() may safely be left empty
return
self
...
...
@@ -5861,9 +5829,6 @@ class SingleAssignmentNode(AssignmentNode):
first
=
False
is_overloaded_assignment
=
False
declaration_only
=
False
needs_unlock
=
False
needs_rlock
=
False
needs_wlock
=
False
def
analyse_declarations
(
self
,
env
):
from
.
import
ExprNodes
...
...
@@ -5960,14 +5925,7 @@ class SingleAssignmentNode(AssignmentNode):
self
.
lhs
=
self
.
lhs
.
analyse_target_types
(
env
)
self
.
lhs
.
gil_assignment_check
(
env
)
if
self
.
lhs
.
is_subscript
and
self
.
lhs
.
base
.
type
.
is_cyp_class
:
if
self
.
lhs
.
type
.
is_const
:
# type of the formal 'value' argument of __setitem__
self
.
rhs
.
ensure_rhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
rhs
.
ensure_lhs_locked
(
env
,
is_dereferenced
=
True
)
else
:
self
.
rhs
.
ensure_rhs_locked
(
env
)
self
.
lhs
.
ensure_lhs_locked
(
env
,
is_top_lhs
=
True
)
unrolled_assignment
=
self
.
unroll_lhs
(
env
)
if
unrolled_assignment
:
return
unrolled_assignment
...
...
@@ -6191,11 +6149,10 @@ class CascadedAssignmentNode(AssignmentNode):
for
i
,
lhs
in
enumerate
(
self
.
lhs_list
):
lhs
=
self
.
lhs_list
[
i
]
=
lhs
.
analyse_target_types
(
env
)
lhs
.
gil_assignment_check
(
env
)
lhs
.
ensure_lhs_locked
(
env
,
is_top_lhs
=
True
)
lhs_types
.
add
(
lhs
.
type
)
rhs
=
self
.
rhs
.
analyse_types
(
env
)
rhs
.
ensure_rhs_locked
(
env
)
# common special case: only one type needed on the LHS => coerce only once
if
len
(
lhs_types
)
==
1
:
# Avoid coercion for overloaded assignment operators.
...
...
@@ -6336,9 +6293,7 @@ class InPlaceAssignmentNode(AssignmentNode):
def
analyse_types
(
self
,
env
):
self
.
rhs
=
self
.
rhs
.
analyse_types
(
env
)
self
.
rhs
.
ensure_rhs_locked
(
env
)
self
.
lhs
=
self
.
lhs
.
analyse_target_types
(
env
)
self
.
rhs
.
ensure_lhs_locked
(
env
)
# When assigning to a fully indexed buffer or memoryview, coerce the rhs
if
self
.
lhs
.
is_memview_index
or
self
.
lhs
.
is_buffer_access
:
...
...
@@ -6401,7 +6356,6 @@ class PrintStatNode(StatNode):
stream
=
self
.
stream
.
analyse_expressions
(
env
)
self
.
stream
=
stream
.
coerce_to_pyobject
(
env
)
arg_tuple
=
self
.
arg_tuple
.
analyse_expressions
(
env
)
arg_tuple
.
ensure_rhs_locked
(
env
)
self
.
arg_tuple
=
arg_tuple
.
coerce_to_pyobject
(
env
)
env
.
use_utility_code
(
printing_utility_code
)
if
len
(
self
.
arg_tuple
.
args
)
==
1
and
self
.
append_newline
:
...
...
@@ -6503,7 +6457,6 @@ class DelStatNode(StatNode):
child_attrs
=
[
"args"
]
ignore_nonexisting
=
False
was_locked
=
False
def
analyse_declarations
(
self
,
env
):
for
arg
in
self
.
args
:
...
...
@@ -6515,14 +6468,11 @@ class DelStatNode(StatNode):
arg
=
self
.
args
[
i
]
=
arg
.
analyse_del_expression
(
env
)
else
:
arg
=
self
.
args
[
i
]
=
arg
.
analyse_target_expression
(
env
,
None
)
arg
.
ensure_lhs_locked
(
env
)
if
arg
.
type
.
is_pyobject
or
(
arg
.
is_name
and
arg
.
type
.
is_memoryviewslice
):
if
arg
.
is_name
and
arg
.
entry
.
is_cglobal
:
error
(
arg
.
pos
,
"Deletion of global C variable"
)
elif
arg
.
type
.
is_ptr
and
arg
.
type
.
base_type
.
is_cpp_class
or
arg
.
type
.
is_cyp_class
:
self
.
cpp_check
(
env
)
if
arg
.
type
.
is_cyp_class
:
self
.
was_locked
=
arg
.
needs_rlock
()
or
arg
.
needs_wlock
()
elif
arg
.
type
.
is_cpp_class
:
error
(
arg
.
pos
,
"Deletion of non-heap C++ object"
)
elif
arg
.
is_subscript
and
arg
.
base
.
type
is
Builtin
.
bytearray_type
:
...
...
@@ -6555,11 +6505,8 @@ class DelStatNode(StatNode):
arg
.
free_temps
(
code
)
elif
arg
.
type
.
is_cyp_class
:
arg
.
generate_evaluation_code
(
code
)
if
arg
.
type
.
lock_mode
==
"autolock"
and
self
.
was_locked
:
code
.
putln
(
"Cy_UNLOCK(%s);"
%
arg
.
result
())
else
:
code
.
putln
(
"Cy_DECREF(%s);"
%
arg
.
result
())
code
.
putln
(
"%s = NULL;"
%
arg
.
result
())
code
.
putln
(
"Cy_DECREF(%s);"
%
arg
.
result
())
code
.
putln
(
"%s = NULL;"
%
arg
.
result
())
arg
.
generate_disposal_code
(
code
)
# else error reported earlier
...
...
@@ -6648,7 +6595,6 @@ class ReturnStatNode(StatNode):
if
self
.
in_async_gen
:
error
(
self
.
pos
,
"Return with value in async generator"
)
self
.
value
=
self
.
value
.
analyse_types
(
env
)
self
.
value
.
ensure_rhs_locked
(
env
)
if
return_type
.
is_void
or
return_type
.
is_returncode
:
error
(
self
.
value
.
pos
,
"Return with value in void function"
)
else
:
...
...
@@ -6993,7 +6939,6 @@ class IfClauseNode(Node):
def
analyse_expressions
(
self
,
env
):
self
.
condition
=
self
.
condition
.
analyse_temp_boolean_expression
(
env
)
self
.
condition
.
ensure_rhs_locked
(
env
)
self
.
body
=
self
.
body
.
analyse_expressions
(
env
)
return
self
...
...
@@ -7125,7 +7070,6 @@ class WhileStatNode(LoopNode, StatNode):
def
analyse_expressions
(
self
,
env
):
if
self
.
condition
:
self
.
condition
=
self
.
condition
.
analyse_temp_boolean_expression
(
env
)
self
.
condition
.
ensure_rhs_locked
(
env
)
self
.
body
=
self
.
body
.
analyse_expressions
(
env
)
if
self
.
else_clause
:
self
.
else_clause
=
self
.
else_clause
.
analyse_expressions
(
env
)
...
...
@@ -7357,9 +7301,7 @@ class _ForInStatNode(LoopNode, StatNode):
def
analyse_expressions
(
self
,
env
):
self
.
target
=
self
.
target
.
analyse_target_types
(
env
)
self
.
target
.
ensure_lhs_locked
(
env
)
self
.
iterator
=
self
.
iterator
.
analyse_expressions
(
env
)
self
.
iterator
.
ensure_rhs_locked
(
env
)
self
.
_create_item_node
()
# must rewrap self.item after analysis
self
.
item
=
self
.
item
.
analyse_expressions
(
env
)
if
(
not
self
.
is_async
and
...
...
@@ -7501,17 +7443,13 @@ class ForFromStatNode(LoopNode, StatNode):
def
analyse_expressions
(
self
,
env
):
from
.
import
ExprNodes
self
.
target
=
self
.
target
.
analyse_target_types
(
env
)
self
.
target
.
ensure_lhs_locked
(
env
)
self
.
bound1
=
self
.
bound1
.
analyse_types
(
env
)
self
.
bound1
.
ensure_rhs_locked
(
env
)
self
.
bound2
=
self
.
bound2
.
analyse_types
(
env
)
self
.
bound2
.
ensure_rhs_locked
(
env
)
if
self
.
step
is
not
None
:
if
isinstance
(
self
.
step
,
ExprNodes
.
UnaryMinusNode
):
warning
(
self
.
step
.
pos
,
"Probable infinite loop in for-from-by statement. "
"Consider switching the directions of the relations."
,
2
)
self
.
step
=
self
.
step
.
analyse_types
(
env
)
self
.
step
.
ensure_rhs_locked
(
env
)
self
.
set_up_loop
(
env
)
target_type
=
self
.
target
.
type
...
...
@@ -8622,54 +8560,7 @@ class LockCypclassNode(StatNode):
def
analyse_expressions
(
self
,
env
):
self
.
obj
=
self
.
obj
.
analyse_types
(
env
)
self
.
obj
.
ensure_rhs_locked
(
env
)
if
not
hasattr
(
self
.
obj
,
'entry'
):
error
(
self
.
pos
,
"The (un)locking target has no entry"
)
return
if
not
self
.
obj
.
type
.
is_cyp_class
:
error
(
self
.
pos
,
"Cannot (un)lock a non-cypclass variable !"
)
return
# FIXME: this is a bit redundant here
self
.
obj
.
get_tracked_state
(
env
)
is_rlocked
=
self
.
obj
.
is_rhs_locked
(
env
)
is_wlocked
=
self
.
obj
.
is_lhs_locked
(
env
)
if
self
.
obj
.
type
.
lock_mode
!=
"nolock"
:
if
self
.
state
==
"unclocked"
and
not
(
is_rlocked
or
is_wlocked
):
error
(
self
.
pos
,
"Cannot unlock an already unlocked object !"
)
elif
self
.
state
==
"rlocked"
and
is_rlocked
:
error
(
self
.
pos
,
"Double read lock !"
)
elif
self
.
state
==
"wlocked"
and
is_wlocked
:
error
(
self
.
pos
,
"Double write lock !"
)
# We need to save states because in case of 'with unlocked' statement,
# we must know which lock has to be restored after the with body.
self
.
was_wlocked
=
is_wlocked
self
.
was_rlocked
=
is_rlocked
and
not
is_wlocked
tracked_state
=
self
.
obj
.
tracked_state
if
self
.
state
==
"rlocked"
:
tracked_state
.
is_rlocked
=
True
tracked_state
.
is_wlocked
=
False
elif
self
.
state
==
"wlocked"
:
tracked_state
.
is_rlocked
=
False
tracked_state
.
is_wlocked
=
True
else
:
tracked_state
.
is_rlocked
=
False
tracked_state
.
is_wlocked
=
False
self
.
body
=
self
.
body
.
analyse_expressions
(
env
)
#self.obj.entry.is_rlocked = self.was_rlocked
#self.obj.entry.is_wlocked = self.was_wlocked
tracked_state
.
is_rlocked
=
self
.
was_rlocked
tracked_state
.
is_wlocked
=
self
.
was_wlocked
return
self
def
generate_execution_code
(
self
,
code
):
...
...
Cython/Compiler/Pipeline.py
View file @
5aef3d90
...
...
@@ -141,7 +141,7 @@ def inject_utility_code_stage_factory(context):
def
create_pipeline
(
context
,
mode
,
exclude_classes
=
()):
assert
mode
in
(
'pyx'
,
'py'
,
'pxd'
)
from
.Visitor
import
PrintTree
from
.CypclassTransforms
import
CypclassWrapperInjection
from
.CypclassTransforms
import
CypclassWrapperInjection
,
CypclassLockTransform
from
.ParseTreeTransforms
import
WithTransform
,
NormalizeTree
,
PostParse
,
PxdPostParse
from
.ParseTreeTransforms
import
ForwardDeclareTypes
,
InjectGilHandling
,
AnalyseDeclarationsTransform
from
.ParseTreeTransforms
import
AnalyseExpressionsTransform
,
FindInvalidUseOfFusedTypes
...
...
@@ -212,6 +212,7 @@ def create_pipeline(context, mode, exclude_classes=()):
_check_c_declarations
,
InlineDefNodeCalls
(
context
),
AnalyseExpressionsTransform
(
context
),
CypclassLockTransform
(
context
),
FindInvalidUseOfFusedTypes
(
context
),
ExpandInplaceOperators
(
context
),
IterationTransform
(
context
),
...
...
Cython/Compiler/Symtab.py
View file @
5aef3d90
...
...
@@ -158,10 +158,6 @@ class Entry(object):
# is_fused_specialized boolean Whether this entry of a cdef or def function
# is a specialization
# is_cgetter boolean Is a c-level getter function
# is_wlocked boolean Is locked with a write lock (used for cypclass)
# is_rlocked boolean Is locked with a read lock (used for cypclass)
# needs_rlock boolean The entry needs a read lock (used in autolock mode)
# needs_wlock boolean The entry needs a write lock (used in autolock mode)
#
# is_default boolean This entry is a compiler-generated default and
# is not user-defined (e.g default contructor)
...
...
@@ -173,7 +169,7 @@ class Entry(object):
# mro_index integer The index of the type where this entry was originally
# declared in the mro of the cypclass where it is now
#
# defining_classes
[CypClassType or CppClassType or CStructOrUnionType]
# defining_classes [CypClassType or CppClassType or CStructOrUnionType]
# All the base classes that define an entry that this entry
# overrides, if this entry represents a cypclass method
#
...
...
@@ -251,10 +247,6 @@ class Entry(object):
cf_used
=
True
outer_entry
=
None
is_cgetter
=
False
is_wlocked
=
False
is_rlocked
=
False
needs_rlock
=
False
needs_wlock
=
False
is_default
=
False
mro_index
=
0
from_type
=
None
...
...
@@ -335,14 +327,6 @@ class InnerEntry(Entry):
def
all_entries
(
self
):
return
self
.
defining_entry
.
all_entries
()
class
TrackedLockedEntry
:
def
__init__
(
self
,
entry
,
scope
):
self
.
entry
=
entry
self
.
scope
=
scope
self
.
is_wlocked
=
False
self
.
is_rlocked
=
False
self
.
needs_wlock
=
False
self
.
needs_rlock
=
False
class
Scope
(
object
):
# name string Unqualified name
...
...
@@ -357,7 +341,6 @@ class Scope(object):
# cfunc_entries [Entry] C function entries
# c_class_entries [Entry] All extension type entries
# cypclass_entries [Entry] All cypclass entries
# autolocked_nodes [ExprNodes] All autolocked nodes that needs unlocking
# cname_to_entry {string : Entry} Temp cname to entry mapping
# return_type PyrexType or None Return type of function owning scope
# is_builtin_scope boolean Is the builtin scope of Python/Cython
...
...
@@ -420,7 +403,6 @@ class Scope(object):
self
.
c_class_entries
=
[]
self
.
cypclass_entries
=
[]
self
.
defined_c_classes
=
[]
self
.
autolocked_nodes
=
[]
self
.
imported_c_classes
=
{}
self
.
cname_to_entry
=
{}
self
.
identifier_to_entry
=
{}
...
...
@@ -429,7 +411,6 @@ class Scope(object):
self
.
buffer_entries
=
[]
self
.
lambda_defs
=
[]
self
.
id_counters
=
{}
self
.
tracked_entries
=
{}
def
__deepcopy__
(
self
,
memo
):
...
...
@@ -525,18 +506,6 @@ class Scope(object):
for
e
,
s
in
cypclass_scope
.
iter_cypclass_entries_and_scopes
():
yield
e
,
s
def
declare_tracked
(
self
,
entry
):
# Keying only with the name is wrong: if we have multiple attributes
# with the same name in different cypclass, this will conflict.
key
=
entry
self
.
tracked_entries
[
key
]
=
TrackedLockedEntry
(
entry
,
self
)
return
self
.
tracked_entries
[
key
]
def
lookup_tracked
(
self
,
entry
):
# We don't chain up the scopes on purpose: we want to keep things local
key
=
entry
return
self
.
tracked_entries
.
get
(
key
,
None
)
def
declare
(
self
,
name
,
cname
,
type
,
pos
,
visibility
,
shadow
=
0
,
is_type
=
0
,
create_wrapper
=
0
,
from_type
=
None
):
# Create new entry, and add to dictionary if
# name is not None. Reports a warning if already
...
...
@@ -2093,12 +2062,6 @@ class ModuleScope(Scope):
from
.TypeInference
import
PyObjectTypeInferer
PyObjectTypeInferer
().
infer_types
(
self
)
def
declare_autolocked
(
self
,
node
):
# Add an entry for autolocked cypclass
if
not
(
node
.
type
.
is_cyp_class
and
node
.
type
.
lock_mode
==
"autolock"
):
error
(
node
.
pos
,
"Trying to autolock a non (autolocked) cypclass object !"
)
self
.
autolocked_nodes
.
append
(
node
)
class
LocalScope
(
Scope
):
...
...
@@ -2125,20 +2088,10 @@ class LocalScope(Scope):
if
type
.
is_pyobject
:
entry
.
init
=
"0"
entry
.
is_arg
=
1
if
type
.
is_cyp_class
and
type
.
lock_mode
!=
"nolock"
:
arg_lock_state
=
self
.
declare_tracked
(
entry
)
arg_lock_state
.
is_rlocked
=
type
.
is_const
arg_lock_state
.
is_wlocked
=
not
type
.
is_const
#entry.borrowed = 1 # Not using borrowed arg refs for now
self
.
arg_entries
.
append
(
entry
)
return
entry
def
declare_autolocked
(
self
,
node
):
# Add an entry for autolocked cypclass
if
not
(
node
.
type
.
is_cyp_class
and
node
.
type
.
lock_mode
==
"autolock"
):
error
(
node
.
pos
,
"Trying to autolock a non (autolocked) cypclass object !"
)
self
.
autolocked_nodes
.
append
(
node
)
def
declare_var
(
self
,
name
,
type
,
pos
,
cname
=
None
,
visibility
=
'private'
,
api
=
0
,
in_pxd
=
0
,
is_cdef
=
0
):
...
...
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