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
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
66bbffdd
Commit
66bbffdd
authored
Feb 22, 2013
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor __class__ replacement hack, argument coercion and self-arg checking in AttributeNode
parent
960bfa1c
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
131 additions
and
90 deletions
+131
-90
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+131
-90
No files found.
Cython/Compiler/ExprNodes.py
View file @
66bbffdd
...
@@ -411,7 +411,7 @@ class ExprNode(Node):
...
@@ -411,7 +411,7 @@ class ExprNode(Node):
def
analyse_as_extension_type
(
self
,
env
):
def
analyse_as_extension_type
(
self
,
env
):
# If this node can be interpreted as a reference to an
# If this node can be interpreted as a reference to an
# extension type, return its type, else None.
# extension type
or builtin type
, return its type, else None.
return
None
return
None
def
analyse_types
(
self
,
env
):
def
analyse_types
(
self
,
env
):
...
@@ -783,6 +783,23 @@ class ExprNode(Node):
...
@@ -783,6 +783,23 @@ class ExprNode(Node):
else
:
else
:
return
self
return
self
@
classmethod
def
from_node
(
cls
,
node
,
**
kwargs
):
"""Instantiate this node class from another node, properly
copying over all attributes that one would forget otherwise.
"""
attributes
=
"cf_state cf_maybe_null cf_is_null"
.
split
()
for
attr_name
in
attributes
:
if
attr_name
in
kwargs
:
continue
try
:
value
=
getattr
(
node
,
attr_name
)
except
AttributeError
:
pass
else
:
kwargs
[
attr_name
]
=
value
return
cls
(
node
.
pos
,
**
kwargs
)
class
AtomicExprNode
(
ExprNode
):
class
AtomicExprNode
(
ExprNode
):
# Abstract base class for expression nodes which have
# Abstract base class for expression nodes which have
...
@@ -1476,10 +1493,10 @@ class NameNode(AtomicExprNode):
...
@@ -1476,10 +1493,10 @@ class NameNode(AtomicExprNode):
entry
=
self
.
entry
entry
=
self
.
entry
if
not
entry
:
if
not
entry
:
entry
=
env
.
lookup
(
self
.
name
)
entry
=
env
.
lookup
(
self
.
name
)
if
entry
and
entry
.
is_type
and
entry
.
type
.
is_extension_type
:
if
entry
and
entry
.
is_type
:
return
entry
.
type
if
entry
.
type
.
is_extension_type
:
# or entry.type.is_builtin_type:
else
:
return
entry
.
type
return
None
return
None
def
analyse_target_declaration
(
self
,
env
):
def
analyse_target_declaration
(
self
,
env
):
if
not
self
.
entry
:
if
not
self
.
entry
:
...
@@ -1557,6 +1574,7 @@ class NameNode(AtomicExprNode):
...
@@ -1557,6 +1574,7 @@ class NameNode(AtomicExprNode):
self
.
is_temp
=
False
self
.
is_temp
=
False
self
.
is_used_as_rvalue
=
True
self
.
is_used_as_rvalue
=
True
self
.
use_managed_ref
=
True
self
.
use_managed_ref
=
True
return
self
def
nogil_check
(
self
,
env
):
def
nogil_check
(
self
,
env
):
self
.
nogil
=
True
self
.
nogil
=
True
...
@@ -3889,23 +3907,6 @@ class SimpleCallNode(CallNode):
...
@@ -3889,23 +3907,6 @@ class SimpleCallNode(CallNode):
self
.
is_temp
=
1
self
.
is_temp
=
1
else
:
else
:
self
.
args
=
[
arg
.
analyse_types
(
env
)
for
arg
in
self
.
args
]
self
.
args
=
[
arg
.
analyse_types
(
env
)
for
arg
in
self
.
args
]
if
self
.
self
and
func_type
.
args
:
# Coerce 'self' to the type expected by the method.
self_arg
=
func_type
.
args
[
0
]
if
self_arg
.
not_none
:
# C methods must do the None test for self at *call* time
self
.
self
=
self
.
self
.
as_none_safe_node
(
"'NoneType' object has no attribute '%s'"
,
error
=
'PyExc_AttributeError'
,
format_args
=
[
self
.
function
.
entry
.
name
])
expected_type
=
self_arg
.
type
if
self_arg
.
accept_builtin_subtypes
:
self
.
coerced_self
=
CMethodSelfCloneNode
(
self
.
self
)
else
:
self
.
coerced_self
=
CloneNode
(
self
.
self
)
self
.
coerced_self
=
self
.
coerced_self
.
coerce_to
(
expected_type
,
env
)
# Insert coerced 'self' argument into argument list.
self
.
args
.
insert
(
0
,
self
.
coerced_self
)
self
.
analyse_c_function_call
(
env
)
self
.
analyse_c_function_call
(
env
)
return
self
return
self
...
@@ -3925,6 +3926,11 @@ class SimpleCallNode(CallNode):
...
@@ -3925,6 +3926,11 @@ class SimpleCallNode(CallNode):
self
.
type
=
error_type
self
.
type
=
error_type
return
return
if
self
.
self
:
args
=
[
self
.
self
]
+
self
.
args
else
:
args
=
self
.
args
if
self
.
function
.
type
.
is_cpp_class
:
if
self
.
function
.
type
.
is_cpp_class
:
overloaded_entry
=
self
.
function
.
type
.
scope
.
lookup
(
"operator()"
)
overloaded_entry
=
self
.
function
.
type
.
scope
.
lookup
(
"operator()"
)
if
overloaded_entry
is
None
:
if
overloaded_entry
is
None
:
...
@@ -3946,7 +3952,7 @@ class SimpleCallNode(CallNode):
...
@@ -3946,7 +3952,7 @@ class SimpleCallNode(CallNode):
else
:
else
:
alternatives
=
overloaded_entry
.
all_alternatives
()
alternatives
=
overloaded_entry
.
all_alternatives
()
entry
=
PyrexTypes
.
best_match
(
self
.
args
,
alternatives
,
self
.
pos
,
env
)
entry
=
PyrexTypes
.
best_match
(
args
,
alternatives
,
self
.
pos
,
env
)
if
not
entry
:
if
not
entry
:
self
.
type
=
PyrexTypes
.
error_type
self
.
type
=
PyrexTypes
.
error_type
...
@@ -3958,24 +3964,55 @@ class SimpleCallNode(CallNode):
...
@@ -3958,24 +3964,55 @@ class SimpleCallNode(CallNode):
self
.
function
.
type
=
entry
.
type
self
.
function
.
type
=
entry
.
type
func_type
=
self
.
function_type
()
func_type
=
self
.
function_type
()
else
:
else
:
entry
=
None
func_type
=
self
.
function_type
()
func_type
=
self
.
function_type
()
if
not
func_type
.
is_cfunction
:
if
not
func_type
.
is_cfunction
:
error
(
self
.
pos
,
"Calling non-function type '%s'"
%
func_type
)
error
(
self
.
pos
,
"Calling non-function type '%s'"
%
func_type
)
self
.
type
=
PyrexTypes
.
error_type
self
.
type
=
PyrexTypes
.
error_type
self
.
result_code
=
"<error>"
self
.
result_code
=
"<error>"
return
return
# Check no. of args
# Check no. of args
max_nargs
=
len
(
func_type
.
args
)
max_nargs
=
len
(
func_type
.
args
)
expected_nargs
=
max_nargs
-
func_type
.
optional_arg_count
expected_nargs
=
max_nargs
-
func_type
.
optional_arg_count
actual_nargs
=
len
(
self
.
args
)
actual_nargs
=
len
(
args
)
if
func_type
.
optional_arg_count
and
expected_nargs
!=
actual_nargs
:
if
func_type
.
optional_arg_count
and
expected_nargs
!=
actual_nargs
:
self
.
has_optional_args
=
1
self
.
has_optional_args
=
1
self
.
is_temp
=
1
self
.
is_temp
=
1
# check 'self' argument
if
entry
and
entry
.
is_cmethod
and
func_type
.
args
:
formal_arg
=
func_type
.
args
[
0
]
arg
=
args
[
0
]
if
formal_arg
.
not_none
:
if
self
.
self
:
self
.
self
=
self
.
self
.
as_none_safe_node
(
"'NoneType' object has no attribute '%s'"
,
error
=
'PyExc_AttributeError'
,
format_args
=
[
entry
.
name
])
else
:
# unbound method
arg
=
arg
.
as_none_safe_node
(
"descriptor '%s' requires a '%s' object but received a 'NoneType'"
,
format_args
=
[
entry
.
name
,
formal_arg
.
type
.
name
])
if
self
.
self
:
if
formal_arg
.
accept_builtin_subtypes
:
arg
=
CMethodSelfCloneNode
(
self
.
self
)
else
:
arg
=
CloneNode
(
self
.
self
)
arg
=
self
.
coerced_self
=
arg
.
coerce_to
(
formal_arg
.
type
,
env
)
args
[
0
]
=
arg
# Coerce arguments
# Coerce arguments
some_args_in_temps
=
False
some_args_in_temps
=
False
for
i
in
xrange
(
min
(
max_nargs
,
actual_nargs
)):
for
i
in
xrange
(
min
(
max_nargs
,
actual_nargs
)):
formal_type
=
func_type
.
args
[
i
].
type
formal_arg
=
func_type
.
args
[
i
]
arg
=
self
.
args
[
i
].
coerce_to
(
formal_type
,
env
)
formal_type
=
formal_arg
.
type
arg
=
args
[
i
].
coerce_to
(
formal_type
,
env
)
if
formal_arg
.
not_none
:
# C methods must do the None checks at *call* time
arg
=
arg
.
as_none_safe_node
(
"cannot pass None into a C function argument that is declared 'not None'"
)
if
arg
.
is_temp
:
if
arg
.
is_temp
:
if
i
>
0
:
if
i
>
0
:
# first argument in temp doesn't impact subsequent arguments
# first argument in temp doesn't impact subsequent arguments
...
@@ -3995,19 +4032,21 @@ class SimpleCallNode(CallNode):
...
@@ -3995,19 +4032,21 @@ class SimpleCallNode(CallNode):
if
i
>
0
:
# first argument doesn't matter
if
i
>
0
:
# first argument doesn't matter
some_args_in_temps
=
True
some_args_in_temps
=
True
arg
=
arg
.
coerce_to_temp
(
env
)
arg
=
arg
.
coerce_to_temp
(
env
)
self
.
args
[
i
]
=
arg
args
[
i
]
=
arg
# handle additional varargs parameters
# handle additional varargs parameters
for
i
in
xrange
(
max_nargs
,
actual_nargs
):
for
i
in
xrange
(
max_nargs
,
actual_nargs
):
arg
=
self
.
args
[
i
]
arg
=
args
[
i
]
if
arg
.
type
.
is_pyobject
:
if
arg
.
type
.
is_pyobject
:
arg_ctype
=
arg
.
type
.
default_coerced_ctype
()
arg_ctype
=
arg
.
type
.
default_coerced_ctype
()
if
arg_ctype
is
None
:
if
arg_ctype
is
None
:
error
(
self
.
args
[
i
].
pos
,
error
(
self
.
args
[
i
].
pos
,
"Python object cannot be passed as a varargs parameter"
)
"Python object cannot be passed as a varargs parameter"
)
else
:
else
:
self
.
args
[
i
]
=
arg
=
arg
.
coerce_to
(
arg_ctype
,
env
)
args
[
i
]
=
arg
=
arg
.
coerce_to
(
arg_ctype
,
env
)
if
arg
.
is_temp
and
i
>
0
:
if
arg
.
is_temp
and
i
>
0
:
some_args_in_temps
=
True
some_args_in_temps
=
True
if
some_args_in_temps
:
if
some_args_in_temps
:
# if some args are temps and others are not, they may get
# if some args are temps and others are not, they may get
# constructed in the wrong order (temps first) => make
# constructed in the wrong order (temps first) => make
...
@@ -4017,7 +4056,7 @@ class SimpleCallNode(CallNode):
...
@@ -4017,7 +4056,7 @@ class SimpleCallNode(CallNode):
for
i
in
xrange
(
actual_nargs
-
1
):
for
i
in
xrange
(
actual_nargs
-
1
):
if
i
==
0
and
self
.
self
is
not
None
:
if
i
==
0
and
self
.
self
is
not
None
:
continue
# self is ok
continue
# self is ok
arg
=
self
.
args
[
i
]
arg
=
args
[
i
]
if
arg
.
nonlocally_immutable
():
if
arg
.
nonlocally_immutable
():
# locals, C functions, unassignable types are safe.
# locals, C functions, unassignable types are safe.
pass
pass
...
@@ -4036,6 +4075,8 @@ class SimpleCallNode(CallNode):
...
@@ -4036,6 +4075,8 @@ class SimpleCallNode(CallNode):
warning
(
arg
.
pos
,
"Argument evaluation order in C function call is undefined and may not be as expected"
,
0
)
warning
(
arg
.
pos
,
"Argument evaluation order in C function call is undefined and may not be as expected"
,
0
)
break
break
self
.
args
[:]
=
args
# Calc result type and code fragment
# Calc result type and code fragment
if
isinstance
(
self
.
function
,
NewExprNode
):
if
isinstance
(
self
.
function
,
NewExprNode
):
self
.
type
=
PyrexTypes
.
CPtrType
(
self
.
function
.
class_type
)
self
.
type
=
PyrexTypes
.
CPtrType
(
self
.
function
.
class_type
)
...
@@ -4657,19 +4698,21 @@ class AttributeNode(ExprNode):
...
@@ -4657,19 +4698,21 @@ class AttributeNode(ExprNode):
return
self
.
obj
.
type_dependencies
(
env
)
return
self
.
obj
.
type_dependencies
(
env
)
def
infer_type
(
self
,
env
):
def
infer_type
(
self
,
env
):
if
self
.
analyse_as_cimported_attribute
(
env
,
0
):
# FIXME: this is way too redundant with analyse_types()
return
self
.
entry
.
type
node
=
self
.
analyse_as_cimported_attribute_node
(
env
,
target
=
False
)
elif
self
.
analyse_as_unbound_cmethod
(
env
):
if
node
is
not
None
:
return
self
.
entry
.
type
return
node
.
entry
.
type
else
:
node
=
self
.
analyse_as_unbound_cmethod_node
(
env
)
obj_type
=
self
.
obj
.
infer_type
(
env
)
if
node
is
not
None
:
self
.
analyse_attribute
(
env
,
obj_type
=
obj_type
)
return
node
.
entry
.
type
if
obj_type
.
is_builtin_type
and
self
.
type
.
is_cfunction
:
obj_type
=
self
.
obj
.
infer_type
(
env
)
# special case: C-API replacements for C methods of
self
.
analyse_attribute
(
env
,
obj_type
=
obj_type
)
# builtin types cannot be inferred as C functions as
if
obj_type
.
is_builtin_type
and
self
.
type
.
is_cfunction
:
# that would prevent their use as bound methods
# special case: C-API replacements for C methods of
return
py_object_type
# builtin types cannot be inferred as C functions as
return
self
.
type
# that would prevent their use as bound methods
return
py_object_type
return
self
.
type
def
analyse_target_declaration
(
self
,
env
):
def
analyse_target_declaration
(
self
,
env
):
pass
pass
...
@@ -4684,21 +4727,19 @@ class AttributeNode(ExprNode):
...
@@ -4684,21 +4727,19 @@ class AttributeNode(ExprNode):
def
analyse_types
(
self
,
env
,
target
=
0
):
def
analyse_types
(
self
,
env
,
target
=
0
):
self
.
initialized_check
=
env
.
directives
[
'initializedcheck'
]
self
.
initialized_check
=
env
.
directives
[
'initializedcheck'
]
if
self
.
analyse_as_cimported_attribute
(
env
,
target
):
node
=
self
.
analyse_as_cimported_attribute_node
(
env
,
target
)
self
.
entry
.
used
=
True
if
node
is
None
and
not
target
:
elif
not
target
and
self
.
analyse_as_unbound_cmethod
(
env
):
node
=
self
.
analyse_as_unbound_cmethod_node
(
env
)
self
.
entry
.
used
=
True
if
node
is
None
:
else
:
node
=
self
.
analyse_as_ordinary_attribute_node
(
env
,
target
)
self
.
analyse_as_ordinary_attribute
(
env
,
target
)
assert
node
is
not
None
if
self
.
entry
:
if
node
.
entry
:
self
.
entry
.
used
=
True
node
.
entry
.
used
=
True
if
node
.
is_attribute
:
# may be mutated in a namenode now :)
node
.
wrap_obj_in_nonecheck
(
env
)
if
self
.
is_attribute
:
return
node
self
.
wrap_obj_in_nonecheck
(
env
)
return
self
def
analyse_as_cimported_attribute
(
self
,
env
,
target
):
def
analyse_as_cimported_attribute
_node
(
self
,
env
,
target
):
# Try to interpret this as a reference to an imported
# Try to interpret this as a reference to an imported
# C const, type, var or function. If successful, mutates
# C const, type, var or function. If successful, mutates
# this node into a NameNode and returns 1, otherwise
# this node into a NameNode and returns 1, otherwise
...
@@ -4707,33 +4748,33 @@ class AttributeNode(ExprNode):
...
@@ -4707,33 +4748,33 @@ class AttributeNode(ExprNode):
if
module_scope
:
if
module_scope
:
entry
=
module_scope
.
lookup_here
(
self
.
attribute
)
entry
=
module_scope
.
lookup_here
(
self
.
attribute
)
if
entry
and
(
if
entry
and
(
entry
.
is_cglobal
or
entry
.
is_cfunction
entry
.
is_cglobal
or
entry
.
is_cfunction
or
entry
.
is_type
or
entry
.
is_const
):
or
entry
.
is_type
or
entry
.
is_const
):
self
.
mutate_into_name_node
(
env
,
entry
,
target
)
# FIXME
return
self
.
as_name_node
(
env
,
entry
,
target
)
entry
.
used
=
1
return
None
return
1
return
0
def
analyse_as_unbound_cmethod
(
self
,
env
):
def
analyse_as_unbound_cmethod
_node
(
self
,
env
):
# Try to interpret this as a reference to an unbound
# Try to interpret this as a reference to an unbound
# C method of an extension type
. If successful, mutates
# C method of an extension type
or builtin type. If successful,
#
this node into a NameNode and returns 1
, otherwise
#
creates a corresponding NameNode and returns it
, otherwise
# returns
0
.
# returns
None
.
type
=
self
.
obj
.
analyse_as_extension_type
(
env
)
type
=
self
.
obj
.
analyse_as_extension_type
(
env
)
if
type
:
if
type
:
entry
=
type
.
scope
.
lookup_here
(
self
.
attribute
)
entry
=
type
.
scope
.
lookup_here
(
self
.
attribute
)
if
entry
and
entry
.
is_cmethod
:
if
entry
and
entry
.
is_cmethod
:
# Create a temporary entry describing the C method
if
type
.
is_builtin_type
:
# as an ordinary function.
ubcm_entry
=
entry
ubcm_entry
=
Symtab
.
Entry
(
entry
.
name
,
else
:
"%s->%s"
%
(
type
.
vtabptr_cname
,
entry
.
cname
),
# Create a temporary entry describing the C method
entry
.
type
)
# as an ordinary function.
ubcm_entry
.
is_cfunction
=
1
ubcm_entry
=
Symtab
.
Entry
(
entry
.
name
,
ubcm_entry
.
func_cname
=
entry
.
func_cname
"%s->%s"
%
(
type
.
vtabptr_cname
,
entry
.
cname
),
ubcm_entry
.
is_unbound_cmethod
=
1
entry
.
type
)
self
.
mutate_into_name_node
(
env
,
ubcm_entry
,
None
)
# FIXME
ubcm_entry
.
is_cfunction
=
1
return
1
ubcm_entry
.
func_cname
=
entry
.
func_cname
return
0
ubcm_entry
.
is_unbound_cmethod
=
1
return
self
.
as_name_node
(
env
,
ubcm_entry
,
target
=
False
)
return
None
def
analyse_as_type
(
self
,
env
):
def
analyse_as_type
(
self
,
env
):
module_scope
=
self
.
obj
.
analyse_as_module
(
env
)
module_scope
=
self
.
obj
.
analyse_as_module
(
env
)
...
@@ -4751,8 +4792,9 @@ class AttributeNode(ExprNode):
...
@@ -4751,8 +4792,9 @@ class AttributeNode(ExprNode):
module_scope
=
self
.
obj
.
analyse_as_module
(
env
)
module_scope
=
self
.
obj
.
analyse_as_module
(
env
)
if
module_scope
:
if
module_scope
:
entry
=
module_scope
.
lookup_here
(
self
.
attribute
)
entry
=
module_scope
.
lookup_here
(
self
.
attribute
)
if
entry
and
entry
.
is_type
and
entry
.
type
.
is_extension_type
:
if
entry
and
entry
.
is_type
:
return
entry
.
type
if
entry
.
type
.
is_extension_type
:
# or entry.type.is_builtin_type:
return
entry
.
type
return
None
return
None
def
analyse_as_module
(
self
,
env
):
def
analyse_as_module
(
self
,
env
):
...
@@ -4765,20 +4807,18 @@ class AttributeNode(ExprNode):
...
@@ -4765,20 +4807,18 @@ class AttributeNode(ExprNode):
return
entry
.
as_module
return
entry
.
as_module
return
None
return
None
def
mutate_into
_name_node
(
self
,
env
,
entry
,
target
):
def
as
_name_node
(
self
,
env
,
entry
,
target
):
#
Mutate this node into a NameN
ode and complete the
#
Create a corresponding NameNode from this n
ode and complete the
# analyse_types phase.
# analyse_types phase.
self
.
__class__
=
NameNode
node
=
NameNode
.
from_node
(
self
,
name
=
self
.
attribute
,
entry
=
entry
)
self
.
name
=
self
.
attribute
self
.
entry
=
entry
del
self
.
obj
del
self
.
attribute
if
target
:
if
target
:
NameNode
.
analyse_target_types
(
self
,
env
)
# FIXME
node
=
node
.
analyse_target_types
(
env
)
else
:
else
:
NameNode
.
analyse_rvalue_entry
(
self
,
env
)
node
=
node
.
analyse_rvalue_entry
(
env
)
node
.
entry
.
used
=
1
return
node
def
analyse_as_ordinary_attribute
(
self
,
env
,
target
):
def
analyse_as_ordinary_attribute
_node
(
self
,
env
,
target
):
self
.
obj
=
self
.
obj
.
analyse_types
(
env
)
self
.
obj
=
self
.
obj
.
analyse_types
(
env
)
self
.
analyse_attribute
(
env
)
self
.
analyse_attribute
(
env
)
if
self
.
entry
and
self
.
entry
.
is_cmethod
and
not
self
.
is_called
:
if
self
.
entry
and
self
.
entry
.
is_cmethod
and
not
self
.
is_called
:
...
@@ -4795,6 +4835,7 @@ class AttributeNode(ExprNode):
...
@@ -4795,6 +4835,7 @@ class AttributeNode(ExprNode):
error
(
self
.
pos
,
"Assignment to an immutable object field"
)
error
(
self
.
pos
,
"Assignment to an immutable object field"
)
#elif self.type.is_memoryviewslice and not target:
#elif self.type.is_memoryviewslice and not target:
# self.is_temp = True
# self.is_temp = True
return
self
def
analyse_attribute
(
self
,
env
,
obj_type
=
None
):
def
analyse_attribute
(
self
,
env
,
obj_type
=
None
):
# Look up attribute and set self.type and self.member.
# Look up attribute and set self.type and self.member.
...
...
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