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
6094ac85
Commit
6094ac85
authored
Jul 31, 2020
by
Xavier Thompson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reorganise cypclass wrapper injection code
parent
341223c4
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
1058 additions
and
5 deletions
+1058
-5
Cython/Compiler/CypclassTransforms.py
Cython/Compiler/CypclassTransforms.py
+446
-0
Cython/Compiler/ModuleNode.py
Cython/Compiler/ModuleNode.py
+599
-4
Cython/Compiler/Pipeline.py
Cython/Compiler/Pipeline.py
+1
-1
Cython/Compiler/Symtab.py
Cython/Compiler/Symtab.py
+12
-0
No files found.
Cython/Compiler/Cypclass
Wrapper
.py
→
Cython/Compiler/Cypclass
Transforms
.py
View file @
6094ac85
#
# C
ode generation for wrapping cypclasses with cclasse
s
# C
ypclass transform
s
#
from
__future__
import
absolute_import
import
cython
cython
.
declare
(
Naming
=
object
,
PyrexTypes
=
object
,
error
=
object
,
warning
=
object
,
EncodedString
=
object
)
cython
.
declare
(
Naming
=
object
,
PyrexTypes
=
object
,
EncodedString
=
object
)
from
collections
import
defaultdict
from
.
import
CypclassWrapper
from
.
import
Naming
from
.
import
Nodes
from
.
import
PyrexTypes
...
...
@@ -18,7 +17,6 @@ from . import ExprNodes
from
.
import
Visitor
from
.
import
TreeFragment
from
.Errors
import
error
,
warning
from
.StringEncoding
import
EncodedString
from
.ParseTreeTransforms
import
NormalizeTree
,
InterpretCompilerDirectives
,
DecoratorTransform
,
AnalyseDeclarationsTransform
...
...
@@ -446,619 +444,3 @@ def NAME(ARGDECLS):
return
method_wrapper
#
# Utilities for cypclasses
#
def
cypclass_iter_scopes
(
scope
):
"""
Recursively iterate over nested cypclasses and their associated scope
"""
for
entry
in
scope
.
cypclass_entries
:
cypclass_scope
=
entry
.
type
.
scope
yield
entry
,
cypclass_scope
if
cypclass_scope
:
for
e
,
s
in
cypclass_iter_scopes
(
cypclass_scope
):
yield
e
,
s
#
# Cypclass code generation
#
# - originally authored by Gwenaël Samain
# - moved here from ModuleNode.py
#
def
generate_cyp_class_deferred_definitions
(
env
,
code
,
definition
):
"""
Generate all cypclass method definitions, deferred till now
"""
for
entry
,
scope
in
cypclass_iter_scopes
(
env
):
if
definition
or
entry
.
defined_in_pxd
:
if
entry
.
type
.
activable
:
# Generate acthon-specific classes
generate_cyp_class_reifying_entries
(
entry
,
code
)
generate_cyp_class_activated_class
(
entry
,
code
)
generate_cyp_class_activate_function
(
entry
,
code
)
# Generate cypclass attr destructor
generate_cyp_class_attrs_destructor_definition
(
entry
,
code
)
# Generate wrapper constructor
wrapper
=
scope
.
lookup_here
(
"<constructor>"
)
constructor
=
scope
.
lookup_here
(
"<init>"
)
new
=
scope
.
lookup_here
(
"__new__"
)
alloc
=
scope
.
lookup_here
(
"<alloc>"
)
for
wrapper_entry
in
wrapper
.
all_alternatives
():
generate_cyp_class_wrapper_definition
(
entry
.
type
,
wrapper_entry
,
constructor
,
new
,
alloc
,
code
)
def
generate_cyp_class_attrs_destructor_definition
(
entry
,
code
):
"""
Generate destructor definition for the given cypclass entry
"""
scope
=
entry
.
type
.
scope
cypclass_attrs
=
[
e
for
e
in
scope
.
var_entries
if
e
.
type
.
is_cyp_class
and
not
e
.
name
==
"this"
and
not
e
.
is_type
]
if
cypclass_attrs
:
cypclass_attrs_destructor_name
=
"%s__cypclass_attrs_destructor__%s"
%
(
Naming
.
func_prefix
,
entry
.
name
)
destructor_with_namespace
=
"void %s::%s()"
%
(
entry
.
type
.
empty_declaration_code
(),
cypclass_attrs_destructor_name
)
code
.
putln
(
destructor_with_namespace
)
code
.
putln
(
"{"
)
for
attr
in
cypclass_attrs
:
code
.
putln
(
"Cy_XDECREF(this->%s);"
%
attr
.
cname
)
code
.
putln
(
"}"
)
def
generate_cyp_class_activate_function
(
entry
,
code
):
"""
Generate activate function for activable cypclass entries
"""
active_self_entry
=
entry
.
type
.
scope
.
lookup_here
(
"<active_self>"
)
dunder_activate_entry
=
entry
.
type
.
scope
.
lookup_here
(
"__activate__"
)
# Here we generate the function header like Nodes.CFuncDefNode would do,
# but we streamline the process because we know the exact prototype.
dunder_activate_arg
=
dunder_activate_entry
.
type
.
op_arg_struct
.
declaration_code
(
Naming
.
optional_args_cname
)
dunder_activate_entity
=
dunder_activate_entry
.
type
.
function_header_code
(
dunder_activate_entry
.
func_cname
,
dunder_activate_arg
)
dunder_activate_header
=
dunder_activate_entry
.
type
.
return_type
.
declaration_code
(
dunder_activate_entity
)
code
.
putln
(
"%s {"
%
dunder_activate_header
)
code
.
putln
(
"%s;"
%
dunder_activate_entry
.
type
.
return_type
.
declaration_code
(
"activated_instance"
))
code
.
putln
(
'if (%s) {'
%
Naming
.
optional_args_cname
)
activated_class_constructor_optargs_list
=
[
"this"
]
activated_class_constructor_defaultargs_list
=
[
"this->_active_queue_class"
,
"this->_active_result_class"
]
for
i
,
arg
in
enumerate
(
dunder_activate_entry
.
type
.
args
):
code
.
putln
(
"if (%s->%sn <= %s) {"
%
(
Naming
.
optional_args_cname
,
Naming
.
pyrex_prefix
,
i
))
code
.
putln
(
"activated_instance = new %s::Activated(%s);"
%
(
entry
.
type
.
empty_declaration_code
(),
", "
.
join
(
activated_class_constructor_optargs_list
+
activated_class_constructor_defaultargs_list
[
i
:])))
code
.
putln
(
"} else {"
)
activated_class_constructor_optargs_list
.
append
(
"%s->%s"
%
(
Naming
.
optional_args_cname
,
dunder_activate_entry
.
type
.
opt_arg_cname
(
arg
.
name
)))
# We're in the final else clause, corresponding to all optional arguments specified)
code
.
putln
(
"activated_instance = new %s::Activated(%s);"
%
(
entry
.
type
.
empty_declaration_code
(),
", "
.
join
(
activated_class_constructor_optargs_list
)))
for
_
in
dunder_activate_entry
.
type
.
args
:
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
code
.
putln
(
"else {"
)
code
.
putln
(
"if (this->%s == NULL) {"
%
active_self_entry
.
cname
)
code
.
putln
(
"this->%s = new %s::Activated(this, %s);"
%
(
active_self_entry
.
cname
,
entry
.
type
.
empty_declaration_code
(),
", "
.
join
(
activated_class_constructor_defaultargs_list
))
)
code
.
putln
(
"}"
)
code
.
putln
(
"Cy_INCREF(this->%s);"
%
active_self_entry
.
cname
)
code
.
putln
(
"activated_instance = this->%s;"
%
active_self_entry
.
cname
)
code
.
putln
(
"}"
)
code
.
putln
(
"return activated_instance;"
)
code
.
putln
(
"}"
)
def
generate_cyp_class_activated_class
(
entry
,
code
):
"""
Generate activated class
"""
from
.
import
Builtin
sync_interface_type
=
Builtin
.
acthon_sync_type
result_interface_type
=
Builtin
.
acthon_result_type
queue_interface_type
=
Builtin
.
acthon_queue_type
result_attr_cname
=
"_active_result_class"
queue_attr_cname
=
"_active_queue_class"
passive_self_attr_cname
=
Naming
.
builtin_prefix
+
entry
.
type
.
empty_declaration_code
().
replace
(
'::'
,
'__'
)
+
"_passive_self"
activable_bases_cnames
=
[
base
.
cname
for
base
in
entry
.
type
.
base_classes
if
base
.
activable
]
activable_bases_inheritance_list
=
[
"public %s::Activated"
%
cname
for
cname
in
activable_bases_cnames
]
if
activable_bases_cnames
:
base_classes_code
=
", "
.
join
(
activable_bases_inheritance_list
)
initialize_code
=
", "
.
join
([
"%s::Activated(passive_object, active_queue, active_result_constructor)"
%
cname
for
cname
in
activable_bases_cnames
])
else
:
base_classes_code
=
"public ActhonActivableClass"
initialize_code
=
"ActhonActivableClass(active_queue, active_result_constructor)"
code
.
putln
(
"struct %s::Activated : %s {"
%
(
entry
.
type
.
empty_declaration_code
(),
base_classes_code
))
code
.
putln
(
"%s;"
%
entry
.
type
.
declaration_code
(
passive_self_attr_cname
))
code
.
putln
((
"Activated(%s * passive_object, %s, %s)"
": %s, %s(passive_object){} // Used by _passive_self.__activate__()"
%
(
entry
.
type
.
empty_declaration_code
(),
queue_interface_type
.
declaration_code
(
"active_queue"
),
entry
.
type
.
scope
.
lookup_here
(
"__activate__"
).
type
.
args
[
1
].
type
.
declaration_code
(
"active_result_constructor"
),
initialize_code
,
passive_self_attr_cname
)
))
for
reifying_class_entry
in
entry
.
type
.
scope
.
reifying_entries
:
reified_function_entry
=
reifying_class_entry
.
reified_entry
code
.
putln
(
"// generating reified of %s"
%
reified_function_entry
.
name
)
reified_arg_cname_list
=
[]
reified_arg_decl_list
=
[]
for
i
in
range
(
len
(
reified_function_entry
.
type
.
args
)
-
reified_function_entry
.
type
.
optional_arg_count
):
arg
=
reified_function_entry
.
type
.
args
[
i
]
reified_arg_cname_list
.
append
(
arg
.
cname
)
reified_arg_decl_list
.
append
(
arg
.
type
.
declaration_code
(
arg
.
cname
))
if
reified_function_entry
.
type
.
optional_arg_count
:
opt_cname
=
Naming
.
optional_args_cname
reified_arg_cname_list
.
append
(
opt_cname
)
reified_arg_decl_list
.
append
(
reified_function_entry
.
type
.
op_arg_struct
.
declaration_code
(
opt_cname
))
activated_method_arg_decl_code
=
", "
.
join
([
sync_interface_type
.
declaration_code
(
"sync_object"
)]
+
reified_arg_decl_list
)
function_header
=
reified_function_entry
.
type
.
function_header_code
(
reified_function_entry
.
cname
,
activated_method_arg_decl_code
)
function_code
=
result_interface_type
.
declaration_code
(
function_header
)
code
.
putln
(
"%s {"
%
function_code
)
code
.
putln
(
"%s = this->%s();"
%
(
result_interface_type
.
declaration_code
(
"result_object"
),
result_attr_cname
))
message_constructor_args_list
=
[
"this->%s"
%
passive_self_attr_cname
,
"sync_object"
,
"result_object"
]
+
reified_arg_cname_list
message_constructor_args_code
=
", "
.
join
(
message_constructor_args_list
)
code
.
putln
(
"%s = new %s(%s);"
%
(
reifying_class_entry
.
type
.
declaration_code
(
"message"
),
reifying_class_entry
.
type
.
empty_declaration_code
(),
message_constructor_args_code
))
code
.
putln
(
"/* Push message in the queue */"
)
code
.
putln
(
"if (this->%s != NULL) {"
%
queue_attr_cname
)
code
.
putln
(
"Cy_WLOCK(%s);"
%
queue_attr_cname
)
code
.
putln
(
"this->%s->push(message);"
%
queue_attr_cname
)
code
.
putln
(
"Cy_UNLOCK(%s);"
%
queue_attr_cname
)
code
.
putln
(
"} else {"
)
code
.
putln
(
"/* We should definitely shout here */"
)
code
.
putln
(
'fprintf(stderr, "Acthon error: No queue to push to for %s remote call !
\
\
n");'
%
reified_function_entry
.
name
)
code
.
putln
(
"}"
)
code
.
putln
(
"Cy_DECREF(message);"
)
code
.
putln
(
"/* Return result object */"
)
code
.
putln
(
"return result_object;"
)
code
.
putln
(
"}"
)
code
.
putln
(
"};"
)
def
generate_cyp_class_reifying_entries
(
entry
,
code
):
"""
Generate code to reify the cypclass entry ? -> TODO what does this do exactly ?
"""
target_object_type
=
entry
.
type
target_object_cname
=
Naming
.
builtin_prefix
+
"target_object"
target_object_code
=
target_object_type
.
declaration_code
(
target_object_cname
)
sync_arg_name
=
"sync_method"
result_arg_name
=
"result_object"
from
.
import
Builtin
message_base_type
=
Builtin
.
acthon_message_type
sync_type
=
Builtin
.
acthon_sync_type
result_type
=
Builtin
.
acthon_result_type
sync_attr_cname
=
message_base_type
.
scope
.
lookup_here
(
"_sync_method"
).
cname
result_attr_cname
=
message_base_type
.
scope
.
lookup_here
(
"_result"
).
cname
def
put_cypclass_op_on_narg_optarg
(
op_lbda
,
func_type
,
opt_arg_name
,
code
):
opt_arg_count
=
func_type
.
optional_arg_count
narg_count
=
len
(
func_type
.
args
)
-
opt_arg_count
for
narg
in
func_type
.
args
[:
narg_count
]:
if
narg
.
type
.
is_cyp_class
:
code
.
putln
(
"%s(this->%s);"
%
(
op_lbda
(
narg
),
narg
.
cname
))
if
opt_arg_count
:
opt_arg_guard
=
code
.
insertion_point
()
code
.
increase_indent
()
num_if
=
0
for
opt_idx
,
optarg
in
enumerate
(
func_type
.
args
[
narg_count
:]):
if
optarg
.
type
.
is_cyp_class
:
code
.
putln
(
"if (this->%s->%sn > %s) {"
%
(
opt_arg_name
,
Naming
.
pyrex_prefix
,
opt_idx
))
code
.
putln
(
"%s(this->%s->%s);"
%
(
op_lbda
(
optarg
),
opt_arg_name
,
func_type
.
opt_arg_cname
(
optarg
.
name
)
))
num_if
+=
1
for
_
in
range
(
num_if
):
code
.
putln
(
"}"
)
if
num_if
:
opt_arg_guard
.
putln
(
"if (this->%s != NULL) {"
%
opt_arg_name
)
code
.
putln
(
"}"
)
else
:
code
.
decrease_indent
()
for
reifying_class_entry
in
entry
.
type
.
scope
.
reifying_entries
:
reified_function_entry
=
reifying_class_entry
.
reified_entry
reifying_class_full_name
=
reifying_class_entry
.
type
.
empty_declaration_code
()
class_name
=
reifying_class_full_name
.
split
(
'::'
)[
-
1
]
code
.
putln
(
"struct %s : public %s {"
%
(
reifying_class_full_name
,
message_base_type
.
empty_declaration_code
()))
# Declaring target object & reified method arguments
code
.
putln
(
"%s;"
%
target_object_code
)
constructor_args_decl_list
=
[
target_object_code
,
sync_type
.
declaration_code
(
sync_arg_name
),
result_type
.
declaration_code
(
result_arg_name
)
]
initialized_args_list
=
[
target_object_cname
]
opt_arg_count
=
reified_function_entry
.
type
.
optional_arg_count
for
i
in
range
(
len
(
reified_function_entry
.
type
.
args
)
-
opt_arg_count
):
arg
=
reified_function_entry
.
type
.
args
[
i
]
arg_cname_code
=
arg
.
type
.
declaration_code
(
arg
.
cname
)
code
.
putln
(
"%s;"
%
arg_cname_code
)
constructor_args_decl_list
.
append
(
arg_cname_code
)
initialized_args_list
.
append
(
arg
.
cname
)
if
opt_arg_count
:
# We cannot initialize the struct before allocating memory, so
# it must be handled in constructor body, not initializer list
opt_decl_code
=
reified_function_entry
.
type
.
op_arg_struct
.
declaration_code
(
Naming
.
optional_args_cname
)
code
.
putln
(
"%s;"
%
opt_decl_code
)
constructor_args_decl_list
.
append
(
opt_decl_code
)
# Putting them into constructor
constructor_args_decl_code
=
", "
.
join
(
constructor_args_decl_list
)
initializer_list
=
[
"%s(%s)"
%
(
name
,
name
)
for
name
in
initialized_args_list
]
initializer_list_code
=
", "
.
join
(
initializer_list
)
code
.
putln
(
"%s(%s) : %s(%s, %s), %s {"
%
(
class_name
,
constructor_args_decl_code
,
message_base_type
.
empty_declaration_code
(),
sync_arg_name
,
result_arg_name
,
initializer_list_code
))
if
opt_arg_count
:
mem_size
=
"sizeof(%s)"
%
reified_function_entry
.
type
.
op_arg_struct
.
base_type
.
empty_declaration_code
()
code
.
putln
(
"if (%s != NULL) {"
%
Naming
.
optional_args_cname
)
code
.
putln
(
"this->%s = (%s) malloc(%s);"
%
(
Naming
.
optional_args_cname
,
reified_function_entry
.
type
.
op_arg_struct
.
empty_declaration_code
(),
mem_size
))
code
.
putln
(
"memcpy(this->%s, %s, %s);"
%
(
Naming
.
optional_args_cname
,
Naming
.
optional_args_cname
,
mem_size
))
code
.
putln
(
"} else {"
)
code
.
putln
(
"this->%s = NULL;"
%
Naming
.
optional_args_cname
)
code
.
putln
(
"}"
)
# Acquire a ref on CyObject, as we don't know when the message will be processed
put_cypclass_op_on_narg_optarg
(
lambda
_
:
"Cy_INCREF"
,
reified_function_entry
.
type
,
Naming
.
optional_args_cname
,
code
)
code
.
putln
(
"Cy_INCREF(this->%s);"
%
target_object_cname
)
code
.
putln
(
"}"
)
code
.
putln
(
"int activate() {"
)
sync_result
=
"sync_result"
code
.
putln
(
"int %s = 0;"
%
sync_result
)
code
.
putln
(
"/* Activate only if its sync object agrees to do so */"
)
code
.
putln
(
"if (this->%s != NULL) {"
%
sync_attr_cname
)
code
.
putln
(
"if (!Cy_TRYRLOCK(this->%s)) {"
%
sync_attr_cname
)
code
.
putln
(
"%s = this->%s->isActivable();"
%
(
sync_result
,
sync_attr_cname
))
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
sync_attr_cname
)
code
.
putln
(
"}"
)
code
.
putln
(
"if (%s == 0) return 0;"
%
sync_result
)
code
.
putln
(
"}"
)
result_assignment
=
""
# Drop the target_object argument to perform the actual method call
reified_call_args_list
=
initialized_args_list
[
1
:]
if
opt_arg_count
:
reified_call_args_list
.
append
(
Naming
.
optional_args_cname
)
# Locking CyObjects
# Here we completely ignore the lock mode (nolock/checklock/autolock)
# because the mode is used for direct calls, when the user have the possibility
# to manually lock or let the compiler handle it.
# Here, the user cannot lock manually, so we're taking the lock automatically.
#put_cypclass_op_on_narg_optarg(lambda arg: "Cy_RLOCK" if arg.type.is_const else "Cy_WLOCK",
# reified_function_entry.type, Naming.optional_args_cname, code)
func_type
=
reified_function_entry
.
type
opt_arg_name
=
Naming
.
optional_args_cname
trylock_result
=
"trylock_result"
failed_trylock
=
"failed_trylock"
code
.
putln
(
"int %s = 0;"
%
trylock_result
)
code
.
putln
(
"int %s = 0;"
%
failed_trylock
)
opt_arg_count
=
func_type
.
optional_arg_count
narg_count
=
len
(
func_type
.
args
)
-
opt_arg_count
num_trylock
=
1
op
=
"Cy_TRYRLOCK"
if
reified_function_entry
.
type
.
is_const_method
else
"Cy_TRYWLOCK"
code
.
putln
(
"%s = %s(this->%s) != 0;"
%
(
failed_trylock
,
op
,
target_object_cname
))
code
.
putln
(
"if (!%s) {"
%
failed_trylock
)
code
.
putln
(
"++%s;"
%
trylock_result
)
for
i
,
narg
in
enumerate
(
func_type
.
args
[:
narg_count
]):
if
narg
.
type
.
is_cyp_class
:
try_op
=
"Cy_TRYRLOCK"
if
narg
.
type
.
is_const
else
"Cy_TRYWLOCK"
code
.
putln
(
"%s = %s(this->%s) != 0;"
%
(
failed_trylock
,
try_op
,
narg
.
cname
))
code
.
putln
(
"if (!%s) {"
%
failed_trylock
)
code
.
putln
(
"++%s;"
%
trylock_result
)
num_trylock
+=
1
num_optional_if
=
0
if
opt_arg_count
:
opt_arg_guard
=
code
.
insertion_point
()
code
.
increase_indent
()
for
opt_idx
,
optarg
in
enumerate
(
func_type
.
args
[
narg_count
:]):
if
optarg
.
type
.
is_cyp_class
:
try_op
=
"Cy_TRYRLOCK"
if
optarg
.
type
.
is_const
else
"Cy_TRYWLOCK"
code
.
putln
(
"if (this->%s->%sn > %s) {"
%
(
opt_arg_name
,
Naming
.
pyrex_prefix
,
opt_idx
,
))
code
.
putln
(
"%s = %s(this->%s->%s) != 0;"
%
(
failed_trylock
,
try_op
,
opt_arg_name
,
func_type
.
opt_arg_cname
(
optarg
.
name
)
))
code
.
putln
(
"if (!%s) {"
%
failed_trylock
)
code
.
putln
(
"++%s;"
%
trylock_result
)
num_optional_if
+=
1
num_trylock
+=
1
for
_
in
range
(
num_optional_if
):
code
.
putln
(
"}"
)
if
num_optional_if
>
0
:
opt_arg_guard
.
putln
(
"if (this->%s != NULL) {"
%
opt_arg_name
)
code
.
putln
(
"}"
)
# The check for optional_args != NULL
else
:
code
.
decrease_indent
()
for
_
in
range
(
num_trylock
):
code
.
putln
(
"}"
)
if
num_trylock
:
# If there is any lock failure, we unlock all and return 0
code
.
putln
(
"if (%s) {"
%
failed_trylock
)
num_unlock
=
0
# Target object first, then arguments
code
.
putln
(
"if (%s > %s) {"
%
(
trylock_result
,
num_unlock
))
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
target_object_cname
)
num_unlock
+=
1
for
i
,
narg
in
enumerate
(
func_type
.
args
[:
narg_count
]):
if
narg
.
type
.
is_cyp_class
:
code
.
putln
(
"if (%s > %s) {"
%
(
trylock_result
,
num_unlock
))
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
narg
.
cname
)
num_unlock
+=
1
if
opt_arg_count
and
num_optional_if
:
code
.
putln
(
"if (this->%s != NULL) {"
%
opt_arg_name
)
for
opt_idx
,
optarg
in
enumerate
(
func_type
.
args
[
narg_count
:]):
if
optarg
.
type
.
is_cyp_class
:
code
.
putln
(
"if (%s > %s) {"
%
(
trylock_result
,
num_unlock
))
code
.
putln
(
"Cy_UNLOCK(this->%s->%s);"
%
(
opt_arg_name
,
func_type
.
opt_arg_cname
(
optarg
.
name
)))
num_unlock
+=
1
# Note: we do not respect the semantic order of end-blocks here for simplification purpose.
# This one is for the "not NULL opt arg" check
code
.
putln
(
"}"
)
# These ones are all the checks for mandatory and optional arguments
for
_
in
range
(
num_unlock
):
code
.
putln
(
"}"
)
code
.
putln
(
"return 0;"
)
code
.
putln
(
"}"
)
does_return
=
reified_function_entry
.
type
.
return_type
is
not
PyrexTypes
.
c_void_type
if
does_return
:
result_assignment
=
"%s = "
%
reified_function_entry
.
type
.
return_type
.
declaration_code
(
"result"
)
code
.
putln
(
"%sthis->%s->%s(%s);"
%
(
result_assignment
,
target_object_cname
,
reified_function_entry
.
cname
,
", "
.
join
(
"this->%s"
%
arg_cname
for
arg_cname
in
reified_call_args_list
)
)
)
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
target_object_cname
)
put_cypclass_op_on_narg_optarg
(
lambda
_
:
"Cy_UNLOCK"
,
reified_function_entry
.
type
,
Naming
.
optional_args_cname
,
code
)
code
.
putln
(
"/* Push result in the result object */"
)
if
does_return
:
code
.
putln
(
"Cy_WLOCK(this->%s);"
%
result_attr_cname
)
if
reified_function_entry
.
type
.
return_type
is
PyrexTypes
.
c_int_type
:
code
.
putln
(
"this->%s->pushIntResult(result);"
%
result_attr_cname
)
else
:
code
.
putln
(
"this->%s->pushVoidStarResult((void*)result);"
%
result_attr_cname
)
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
result_attr_cname
)
code
.
putln
(
"return 1;"
)
code
.
putln
(
"}"
)
# Destructor
code
.
putln
(
"virtual ~%s() {"
%
class_name
)
code
.
putln
(
"Cy_DECREF(this->%s);"
%
target_object_cname
)
put_cypclass_op_on_narg_optarg
(
lambda
_
:
"Cy_DECREF"
,
reified_function_entry
.
type
,
Naming
.
optional_args_cname
,
code
)
if
opt_arg_count
:
code
.
putln
(
"free(this->%s);"
%
Naming
.
optional_args_cname
)
code
.
putln
(
"}"
)
code
.
putln
(
"};"
)
def
generate_cyp_class_wrapper_definition
(
type
,
wrapper_entry
,
constructor_entry
,
new_entry
,
alloc_entry
,
code
):
"""
Generate cypclass constructor wrapper ? -> TODO what does this do exactly ?
"""
if
type
.
templates
:
code
.
putln
(
"template <typename %s>"
%
", class "
.
join
(
[
T
.
empty_declaration_code
()
for
T
in
type
.
templates
]))
init_entry
=
constructor_entry
self_type
=
wrapper_entry
.
type
.
return_type
.
declaration_code
(
''
)
type_string
=
type
.
empty_declaration_code
()
class_name
=
type
.
name
wrapper_cname
=
"%s::%s__constructor__%s"
%
(
type_string
,
Naming
.
func_prefix
,
class_name
)
wrapper_type
=
wrapper_entry
.
type
arg_decls
=
[]
arg_names
=
[]
for
arg
in
wrapper_type
.
args
[:
len
(
wrapper_type
.
args
)
-
wrapper_type
.
optional_arg_count
]:
arg_decl
=
arg
.
declaration_code
()
arg_decls
.
append
(
arg_decl
)
arg_names
.
append
(
arg
.
cname
)
if
wrapper_type
.
optional_arg_count
:
arg_decls
.
append
(
wrapper_type
.
op_arg_struct
.
declaration_code
(
Naming
.
optional_args_cname
))
arg_names
.
append
(
Naming
.
optional_args_cname
)
if
wrapper_type
.
has_varargs
:
# We can't safely handle varargs because we need
# to know where the size argument is to start a va_list
error
(
wrapper_entry
.
pos
,
"Cypclass cannot handle variable arguments constructors, but you can use optional arguments (arg=some_value)"
)
if
not
arg_decls
:
arg_decls
=
[
"void"
]
decl_arg_string
=
', '
.
join
(
arg_decls
)
code
.
putln
(
"%s %s(%s)"
%
(
self_type
,
wrapper_cname
,
decl_arg_string
))
code
.
putln
(
"{"
)
wrapper_arg_types
=
[
arg
.
type
for
arg
in
wrapper_entry
.
type
.
args
]
pos
=
wrapper_entry
.
pos
or
type
.
entry
.
pos
if
new_entry
:
alloc_type
=
alloc_entry
.
type
new_arg_types
=
[
alloc_type
]
+
wrapper_arg_types
new_entry
=
PyrexTypes
.
best_match
(
new_arg_types
,
new_entry
.
all_alternatives
(),
pos
)
if
new_entry
:
alloc_call_string
=
"("
+
new_entry
.
type
.
original_alloc_type
.
type
.
declaration_code
(
""
)
+
") %s"
%
alloc_entry
.
func_cname
new_arg_names
=
[
alloc_call_string
]
+
arg_names
new_arg_string
=
', '
.
join
(
new_arg_names
)
code
.
putln
(
"%s self =(%s) %s(%s);"
%
(
self_type
,
self_type
,
new_entry
.
func_cname
,
new_arg_string
))
else
:
code
.
putln
(
"%s self = %s();"
%
(
self_type
,
alloc_entry
.
func_cname
))
# __new__ can be defined by user and return another type
is_new_return_type
=
not
new_entry
or
new_entry
.
type
.
return_type
==
type
# allocate and initialise PyObject fields
if
is_new_return_type
and
type
.
wrapper_type
:
generate_cypclass_wrapper_allocation
(
code
,
type
.
wrapper_type
)
if
init_entry
:
init_entry
=
PyrexTypes
.
best_match
(
wrapper_arg_types
,
init_entry
.
all_alternatives
(),
None
)
if
init_entry
and
(
is_new_return_type
):
# Calling __init__
max_init_nargs
=
len
(
init_entry
.
type
.
args
)
min_init_nargs
=
max_init_nargs
-
init_entry
.
type
.
optional_arg_count
max_wrapper_nargs
=
len
(
wrapper_entry
.
type
.
args
)
min_wrapper_nargs
=
max_wrapper_nargs
-
wrapper_entry
.
type
.
optional_arg_count
if
min_init_nargs
==
min_wrapper_nargs
:
# The optional arguments begin at the same rank for both function
# => just pass the wrapper opt args structure, and everything will be fine.
if
max_wrapper_nargs
>
min_wrapper_nargs
:
# The wrapper has optional args
arg_names
[
-
1
]
=
"(%s) %s"
%
(
init_entry
.
type
.
op_arg_struct
.
declaration_code
(
''
),
arg_names
[
-
1
])
elif
max_init_nargs
>
min_init_nargs
:
# The wrapper has no optional args but the __init__ function does
arg_names
.
append
(
"(%s) NULL"
%
init_entry
.
type
.
op_arg_struct
.
declaration_code
(
''
))
# else, neither __init__ nor __new__ have optional arguments, nothing to do
elif
min_wrapper_nargs
<
min_init_nargs
:
# It means some args from the wrapper should be at
# their default values, which we cannot know from here,
# so shout and stop, sadly.
error
(
init_entry
.
pos
,
"Could not call this __init__ function because the corresponding __new__ wrapper isn't aware of default values"
)
error
(
wrapper_entry
.
pos
,
"Wrapped __new__ is here (some args passed to __init__ could be at their default values)"
)
elif
min_wrapper_nargs
>
min_init_nargs
:
# Here, the __init__ optional arguments start before
# the __new__ ones. We have to unpack the __new__ opt args struct
# in some variables and then repack in the __init__ opt args struct.
init_opt_args_name_list
=
[
arg
.
cname
for
arg
in
wrapper_entry
.
type
.
args
[
min_init_nargs
:]]
# The first __init__ optional arguments are mandatory
# in the __new__ signature, so they will always appear
# in the __init__ optional arguments structure
init_opt_args_number
=
"init_opt_n"
code
.
putln
(
"int %s = %s;"
%
(
init_opt_args_number
,
min_wrapper_nargs
-
min_init_nargs
))
if
wrapper_entry
.
type
.
optional_arg_count
:
for
i
,
arg
in
enumerate
(
wrapper_entry
.
type
.
args
[
min_wrapper_nargs
:]):
# It's an opt arg => it's not declared in the (c++) function scope => declare a variable for it
arg_name
=
arg
.
cname
code
.
putln
(
"%s;"
%
arg
.
type
.
declaration_code
(
arg_name
))
# Arguments unpacking
optional_struct_name
=
arg_names
.
pop
()
code
.
putln
(
"if (%s) {"
%
optional_struct_name
)
# This is necessary to keep __init__ informed of
# how many optional arguments were explicitely given
code
.
putln
(
"%s += %s->%sn;"
%
(
init_opt_args_number
,
optional_struct_name
,
Naming
.
pyrex_prefix
))
braces_number
=
1
+
max_wrapper_nargs
-
min_wrapper_nargs
for
i
,
arg
in
enumerate
(
wrapper_entry
.
type
.
args
[
min_wrapper_nargs
:]):
code
.
putln
(
"if(%s->%sn > %s) {"
%
(
optional_struct_name
,
Naming
.
pyrex_prefix
,
i
))
code
.
putln
(
"%s = %s->%s;"
%
(
arg
.
cname
,
optional_struct_name
,
wrapper_entry
.
type
.
op_arg_struct
.
base_type
.
scope
.
var_entries
[
i
+
1
].
cname
))
for
_
in
range
(
braces_number
):
code
.
putln
(
'}'
)
# Arguments packing
init_opt_args_struct_name
=
"init_opt_args"
code
.
putln
(
"%s;"
%
init_entry
.
type
.
op_arg_struct
.
base_type
.
declaration_code
(
init_opt_args_struct_name
))
code
.
putln
(
"%s.%sn = %s;"
%
(
init_opt_args_struct_name
,
Naming
.
pyrex_prefix
,
init_opt_args_number
))
for
i
,
arg_name
in
enumerate
(
init_opt_args_name_list
):
# The second tuple member is a bit tricky.
# Actually, the only way we have to precisely know the attribute cname
# which corresponds to the argument in the opt args struct
# is to rely on the declaration order in the struct scope.
# FuncDefNode doesn't do this because it has it's declarator node,
# which is not our case here.
code
.
putln
(
"%s.%s = %s;"
%
(
init_opt_args_struct_name
,
init_entry
.
type
.
opt_arg_cname
(
init_entry
.
type
.
args
[
min_init_nargs
+
i
].
name
),
arg_name
))
arg_names
=
arg_names
[:
min_init_nargs
]
+
[
"&"
+
init_opt_args_struct_name
]
init_arg_string
=
','
.
join
(
arg_names
)
code
.
putln
(
"self->%s(%s);"
%
(
init_entry
.
cname
,
init_arg_string
))
code
.
putln
(
"return self;"
)
code
.
putln
(
"}"
)
def
generate_cypclass_wrapper_allocation
(
code
,
wrapper_type
):
"""
Generate allocation and essential setup of the wrapper object.
The cname of the cyobject is assumed to be 'self'.
The cname 'wrapper' is assumed to be available.
"""
objstruct_cname
=
wrapper_type
.
objstruct_cname
code
.
putln
(
"if (self) {"
)
code
.
putln
(
"%s * wrapper = (%s *) self;"
%
(
objstruct_cname
,
objstruct_cname
))
code
.
putln
(
"Py_REFCNT(wrapper) = 0;"
)
code
.
putln
(
"Py_TYPE(wrapper) = %s;"
%
wrapper_type
.
typeptr_cname
)
code
.
putln
(
"}"
)
Cython/Compiler/ModuleNode.py
View file @
6094ac85
...
...
@@ -673,12 +673,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self
.
generate_exttype_vtabptr_declaration
(
entry
,
code
)
self
.
generate_exttype_final_methods_declaration
(
entry
,
code
)
from
.CypclassWrapper
import
generate_cyp_class_deferred_definitions
for
module
in
modules
:
definition
=
module
is
env
code
.
putln
(
""
)
code
.
putln
(
"/* Deferred definitions for cypclasses */"
)
generate_cyp_class_deferred_definitions
(
env
,
code
,
definition
)
self
.
generate_cyp_class_deferred_definitions
(
env
,
code
,
definition
)
def
generate_declarations_for_modules
(
self
,
env
,
modules
,
globalstate
):
typecode
=
globalstate
[
'type_declarations'
]
...
...
@@ -925,6 +924,603 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code
.
putln
(
"typedef struct %s __pyx_gcc33_%s;"
%
(
name
,
tail
))
def
generate_cyp_class_deferred_definitions
(
self
,
env
,
code
,
definition
):
"""
Generate all cypclass method definitions, deferred till now
"""
for
entry
,
scope
in
env
.
iter_cypclass_entries_and_scopes
():
if
definition
or
entry
.
defined_in_pxd
:
if
entry
.
type
.
activable
:
# Generate acthon-specific classes
self
.
generate_cyp_class_reifying_entries
(
entry
,
code
)
self
.
generate_cyp_class_activated_class
(
entry
,
code
)
self
.
generate_cyp_class_activate_function
(
entry
,
code
)
# Generate cypclass attr destructor
self
.
generate_cyp_class_attrs_destructor_definition
(
entry
,
code
)
# Generate wrapper constructor
wrapper
=
scope
.
lookup_here
(
"<constructor>"
)
constructor
=
scope
.
lookup_here
(
"<init>"
)
new
=
scope
.
lookup_here
(
"__new__"
)
alloc
=
scope
.
lookup_here
(
"<alloc>"
)
for
wrapper_entry
in
wrapper
.
all_alternatives
():
self
.
generate_cyp_class_wrapper_definition
(
entry
.
type
,
wrapper_entry
,
constructor
,
new
,
alloc
,
code
)
def
generate_cyp_class_attrs_destructor_definition
(
self
,
entry
,
code
):
"""
Generate destructor definition for the given cypclass entry
"""
scope
=
entry
.
type
.
scope
cypclass_attrs
=
[
e
for
e
in
scope
.
var_entries
if
e
.
type
.
is_cyp_class
and
not
e
.
name
==
"this"
and
not
e
.
is_type
]
if
cypclass_attrs
:
cypclass_attrs_destructor_name
=
"%s__cypclass_attrs_destructor__%s"
%
(
Naming
.
func_prefix
,
entry
.
name
)
destructor_with_namespace
=
"void %s::%s()"
%
(
entry
.
type
.
empty_declaration_code
(),
cypclass_attrs_destructor_name
)
code
.
putln
(
destructor_with_namespace
)
code
.
putln
(
"{"
)
for
attr
in
cypclass_attrs
:
code
.
putln
(
"Cy_XDECREF(this->%s);"
%
attr
.
cname
)
code
.
putln
(
"}"
)
def
generate_cyp_class_activate_function
(
self
,
entry
,
code
):
"""
Generate activate function for activable cypclass entries
"""
active_self_entry
=
entry
.
type
.
scope
.
lookup_here
(
"<active_self>"
)
dunder_activate_entry
=
entry
.
type
.
scope
.
lookup_here
(
"__activate__"
)
# Here we generate the function header like Nodes.CFuncDefNode would do,
# but we streamline the process because we know the exact prototype.
dunder_activate_arg
=
dunder_activate_entry
.
type
.
op_arg_struct
.
declaration_code
(
Naming
.
optional_args_cname
)
dunder_activate_entity
=
dunder_activate_entry
.
type
.
function_header_code
(
dunder_activate_entry
.
func_cname
,
dunder_activate_arg
)
dunder_activate_header
=
dunder_activate_entry
.
type
.
return_type
.
declaration_code
(
dunder_activate_entity
)
code
.
putln
(
"%s {"
%
dunder_activate_header
)
code
.
putln
(
"%s;"
%
dunder_activate_entry
.
type
.
return_type
.
declaration_code
(
"activated_instance"
))
code
.
putln
(
'if (%s) {'
%
Naming
.
optional_args_cname
)
activated_class_constructor_optargs_list
=
[
"this"
]
activated_class_constructor_defaultargs_list
=
[
"this->_active_queue_class"
,
"this->_active_result_class"
]
for
i
,
arg
in
enumerate
(
dunder_activate_entry
.
type
.
args
):
code
.
putln
(
"if (%s->%sn <= %s) {"
%
(
Naming
.
optional_args_cname
,
Naming
.
pyrex_prefix
,
i
))
code
.
putln
(
"activated_instance = new %s::Activated(%s);"
%
(
entry
.
type
.
empty_declaration_code
(),
", "
.
join
(
activated_class_constructor_optargs_list
+
activated_class_constructor_defaultargs_list
[
i
:])))
code
.
putln
(
"} else {"
)
activated_class_constructor_optargs_list
.
append
(
"%s->%s"
%
(
Naming
.
optional_args_cname
,
dunder_activate_entry
.
type
.
opt_arg_cname
(
arg
.
name
)))
# We're in the final else clause, corresponding to all optional arguments specified)
code
.
putln
(
"activated_instance = new %s::Activated(%s);"
%
(
entry
.
type
.
empty_declaration_code
(),
", "
.
join
(
activated_class_constructor_optargs_list
)))
for
_
in
dunder_activate_entry
.
type
.
args
:
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
code
.
putln
(
"else {"
)
code
.
putln
(
"if (this->%s == NULL) {"
%
active_self_entry
.
cname
)
code
.
putln
(
"this->%s = new %s::Activated(this, %s);"
%
(
active_self_entry
.
cname
,
entry
.
type
.
empty_declaration_code
(),
", "
.
join
(
activated_class_constructor_defaultargs_list
))
)
code
.
putln
(
"}"
)
code
.
putln
(
"Cy_INCREF(this->%s);"
%
active_self_entry
.
cname
)
code
.
putln
(
"activated_instance = this->%s;"
%
active_self_entry
.
cname
)
code
.
putln
(
"}"
)
code
.
putln
(
"return activated_instance;"
)
code
.
putln
(
"}"
)
def
generate_cyp_class_activated_class
(
self
,
entry
,
code
):
"""
Generate activated class
"""
from
.
import
Builtin
sync_interface_type
=
Builtin
.
acthon_sync_type
result_interface_type
=
Builtin
.
acthon_result_type
queue_interface_type
=
Builtin
.
acthon_queue_type
result_attr_cname
=
"_active_result_class"
queue_attr_cname
=
"_active_queue_class"
passive_self_attr_cname
=
Naming
.
builtin_prefix
+
entry
.
type
.
empty_declaration_code
().
replace
(
'::'
,
'__'
)
+
"_passive_self"
activable_bases_cnames
=
[
base
.
cname
for
base
in
entry
.
type
.
base_classes
if
base
.
activable
]
activable_bases_inheritance_list
=
[
"public %s::Activated"
%
cname
for
cname
in
activable_bases_cnames
]
if
activable_bases_cnames
:
base_classes_code
=
", "
.
join
(
activable_bases_inheritance_list
)
initialize_code
=
", "
.
join
([
"%s::Activated(passive_object, active_queue, active_result_constructor)"
%
cname
for
cname
in
activable_bases_cnames
])
else
:
base_classes_code
=
"public ActhonActivableClass"
initialize_code
=
"ActhonActivableClass(active_queue, active_result_constructor)"
code
.
putln
(
"struct %s::Activated : %s {"
%
(
entry
.
type
.
empty_declaration_code
(),
base_classes_code
))
code
.
putln
(
"%s;"
%
entry
.
type
.
declaration_code
(
passive_self_attr_cname
))
code
.
putln
((
"Activated(%s * passive_object, %s, %s)"
": %s, %s(passive_object){} // Used by _passive_self.__activate__()"
%
(
entry
.
type
.
empty_declaration_code
(),
queue_interface_type
.
declaration_code
(
"active_queue"
),
entry
.
type
.
scope
.
lookup_here
(
"__activate__"
).
type
.
args
[
1
].
type
.
declaration_code
(
"active_result_constructor"
),
initialize_code
,
passive_self_attr_cname
)
))
for
reifying_class_entry
in
entry
.
type
.
scope
.
reifying_entries
:
reified_function_entry
=
reifying_class_entry
.
reified_entry
code
.
putln
(
"// generating reified of %s"
%
reified_function_entry
.
name
)
reified_arg_cname_list
=
[]
reified_arg_decl_list
=
[]
for
i
in
range
(
len
(
reified_function_entry
.
type
.
args
)
-
reified_function_entry
.
type
.
optional_arg_count
):
arg
=
reified_function_entry
.
type
.
args
[
i
]
reified_arg_cname_list
.
append
(
arg
.
cname
)
reified_arg_decl_list
.
append
(
arg
.
type
.
declaration_code
(
arg
.
cname
))
if
reified_function_entry
.
type
.
optional_arg_count
:
opt_cname
=
Naming
.
optional_args_cname
reified_arg_cname_list
.
append
(
opt_cname
)
reified_arg_decl_list
.
append
(
reified_function_entry
.
type
.
op_arg_struct
.
declaration_code
(
opt_cname
))
activated_method_arg_decl_code
=
", "
.
join
([
sync_interface_type
.
declaration_code
(
"sync_object"
)]
+
reified_arg_decl_list
)
function_header
=
reified_function_entry
.
type
.
function_header_code
(
reified_function_entry
.
cname
,
activated_method_arg_decl_code
)
function_code
=
result_interface_type
.
declaration_code
(
function_header
)
code
.
putln
(
"%s {"
%
function_code
)
code
.
putln
(
"%s = this->%s();"
%
(
result_interface_type
.
declaration_code
(
"result_object"
),
result_attr_cname
))
message_constructor_args_list
=
[
"this->%s"
%
passive_self_attr_cname
,
"sync_object"
,
"result_object"
]
+
reified_arg_cname_list
message_constructor_args_code
=
", "
.
join
(
message_constructor_args_list
)
code
.
putln
(
"%s = new %s(%s);"
%
(
reifying_class_entry
.
type
.
declaration_code
(
"message"
),
reifying_class_entry
.
type
.
empty_declaration_code
(),
message_constructor_args_code
))
code
.
putln
(
"/* Push message in the queue */"
)
code
.
putln
(
"if (this->%s != NULL) {"
%
queue_attr_cname
)
code
.
putln
(
"Cy_WLOCK(%s);"
%
queue_attr_cname
)
code
.
putln
(
"this->%s->push(message);"
%
queue_attr_cname
)
code
.
putln
(
"Cy_UNLOCK(%s);"
%
queue_attr_cname
)
code
.
putln
(
"} else {"
)
code
.
putln
(
"/* We should definitely shout here */"
)
code
.
putln
(
'fprintf(stderr, "Acthon error: No queue to push to for %s remote call !
\
\
n");'
%
reified_function_entry
.
name
)
code
.
putln
(
"}"
)
code
.
putln
(
"Cy_DECREF(message);"
)
code
.
putln
(
"/* Return result object */"
)
code
.
putln
(
"return result_object;"
)
code
.
putln
(
"}"
)
code
.
putln
(
"};"
)
def
generate_cyp_class_reifying_entries
(
self
,
entry
,
code
):
"""
Generate code to reify the cypclass entry ? -> TODO what does this do exactly ?
"""
target_object_type
=
entry
.
type
target_object_cname
=
Naming
.
builtin_prefix
+
"target_object"
target_object_code
=
target_object_type
.
declaration_code
(
target_object_cname
)
sync_arg_name
=
"sync_method"
result_arg_name
=
"result_object"
from
.
import
Builtin
message_base_type
=
Builtin
.
acthon_message_type
sync_type
=
Builtin
.
acthon_sync_type
result_type
=
Builtin
.
acthon_result_type
sync_attr_cname
=
message_base_type
.
scope
.
lookup_here
(
"_sync_method"
).
cname
result_attr_cname
=
message_base_type
.
scope
.
lookup_here
(
"_result"
).
cname
def
put_cypclass_op_on_narg_optarg
(
op_lbda
,
func_type
,
opt_arg_name
,
code
):
opt_arg_count
=
func_type
.
optional_arg_count
narg_count
=
len
(
func_type
.
args
)
-
opt_arg_count
for
narg
in
func_type
.
args
[:
narg_count
]:
if
narg
.
type
.
is_cyp_class
:
code
.
putln
(
"%s(this->%s);"
%
(
op_lbda
(
narg
),
narg
.
cname
))
if
opt_arg_count
:
opt_arg_guard
=
code
.
insertion_point
()
code
.
increase_indent
()
num_if
=
0
for
opt_idx
,
optarg
in
enumerate
(
func_type
.
args
[
narg_count
:]):
if
optarg
.
type
.
is_cyp_class
:
code
.
putln
(
"if (this->%s->%sn > %s) {"
%
(
opt_arg_name
,
Naming
.
pyrex_prefix
,
opt_idx
))
code
.
putln
(
"%s(this->%s->%s);"
%
(
op_lbda
(
optarg
),
opt_arg_name
,
func_type
.
opt_arg_cname
(
optarg
.
name
)
))
num_if
+=
1
for
_
in
range
(
num_if
):
code
.
putln
(
"}"
)
if
num_if
:
opt_arg_guard
.
putln
(
"if (this->%s != NULL) {"
%
opt_arg_name
)
code
.
putln
(
"}"
)
else
:
code
.
decrease_indent
()
for
reifying_class_entry
in
entry
.
type
.
scope
.
reifying_entries
:
reified_function_entry
=
reifying_class_entry
.
reified_entry
reifying_class_full_name
=
reifying_class_entry
.
type
.
empty_declaration_code
()
class_name
=
reifying_class_full_name
.
split
(
'::'
)[
-
1
]
code
.
putln
(
"struct %s : public %s {"
%
(
reifying_class_full_name
,
message_base_type
.
empty_declaration_code
()))
# Declaring target object & reified method arguments
code
.
putln
(
"%s;"
%
target_object_code
)
constructor_args_decl_list
=
[
target_object_code
,
sync_type
.
declaration_code
(
sync_arg_name
),
result_type
.
declaration_code
(
result_arg_name
)
]
initialized_args_list
=
[
target_object_cname
]
opt_arg_count
=
reified_function_entry
.
type
.
optional_arg_count
for
i
in
range
(
len
(
reified_function_entry
.
type
.
args
)
-
opt_arg_count
):
arg
=
reified_function_entry
.
type
.
args
[
i
]
arg_cname_code
=
arg
.
type
.
declaration_code
(
arg
.
cname
)
code
.
putln
(
"%s;"
%
arg_cname_code
)
constructor_args_decl_list
.
append
(
arg_cname_code
)
initialized_args_list
.
append
(
arg
.
cname
)
if
opt_arg_count
:
# We cannot initialize the struct before allocating memory, so
# it must be handled in constructor body, not initializer list
opt_decl_code
=
reified_function_entry
.
type
.
op_arg_struct
.
declaration_code
(
Naming
.
optional_args_cname
)
code
.
putln
(
"%s;"
%
opt_decl_code
)
constructor_args_decl_list
.
append
(
opt_decl_code
)
# Putting them into constructor
constructor_args_decl_code
=
", "
.
join
(
constructor_args_decl_list
)
initializer_list
=
[
"%s(%s)"
%
(
name
,
name
)
for
name
in
initialized_args_list
]
initializer_list_code
=
", "
.
join
(
initializer_list
)
code
.
putln
(
"%s(%s) : %s(%s, %s), %s {"
%
(
class_name
,
constructor_args_decl_code
,
message_base_type
.
empty_declaration_code
(),
sync_arg_name
,
result_arg_name
,
initializer_list_code
))
if
opt_arg_count
:
mem_size
=
"sizeof(%s)"
%
reified_function_entry
.
type
.
op_arg_struct
.
base_type
.
empty_declaration_code
()
code
.
putln
(
"if (%s != NULL) {"
%
Naming
.
optional_args_cname
)
code
.
putln
(
"this->%s = (%s) malloc(%s);"
%
(
Naming
.
optional_args_cname
,
reified_function_entry
.
type
.
op_arg_struct
.
empty_declaration_code
(),
mem_size
))
code
.
putln
(
"memcpy(this->%s, %s, %s);"
%
(
Naming
.
optional_args_cname
,
Naming
.
optional_args_cname
,
mem_size
))
code
.
putln
(
"} else {"
)
code
.
putln
(
"this->%s = NULL;"
%
Naming
.
optional_args_cname
)
code
.
putln
(
"}"
)
# Acquire a ref on CyObject, as we don't know when the message will be processed
put_cypclass_op_on_narg_optarg
(
lambda
_
:
"Cy_INCREF"
,
reified_function_entry
.
type
,
Naming
.
optional_args_cname
,
code
)
code
.
putln
(
"Cy_INCREF(this->%s);"
%
target_object_cname
)
code
.
putln
(
"}"
)
code
.
putln
(
"int activate() {"
)
sync_result
=
"sync_result"
code
.
putln
(
"int %s = 0;"
%
sync_result
)
code
.
putln
(
"/* Activate only if its sync object agrees to do so */"
)
code
.
putln
(
"if (this->%s != NULL) {"
%
sync_attr_cname
)
code
.
putln
(
"if (!Cy_TRYRLOCK(this->%s)) {"
%
sync_attr_cname
)
code
.
putln
(
"%s = this->%s->isActivable();"
%
(
sync_result
,
sync_attr_cname
))
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
sync_attr_cname
)
code
.
putln
(
"}"
)
code
.
putln
(
"if (%s == 0) return 0;"
%
sync_result
)
code
.
putln
(
"}"
)
result_assignment
=
""
# Drop the target_object argument to perform the actual method call
reified_call_args_list
=
initialized_args_list
[
1
:]
if
opt_arg_count
:
reified_call_args_list
.
append
(
Naming
.
optional_args_cname
)
# Locking CyObjects
# Here we completely ignore the lock mode (nolock/checklock/autolock)
# because the mode is used for direct calls, when the user have the possibility
# to manually lock or let the compiler handle it.
# Here, the user cannot lock manually, so we're taking the lock automatically.
#put_cypclass_op_on_narg_optarg(lambda arg: "Cy_RLOCK" if arg.type.is_const else "Cy_WLOCK",
# reified_function_entry.type, Naming.optional_args_cname, code)
func_type
=
reified_function_entry
.
type
opt_arg_name
=
Naming
.
optional_args_cname
trylock_result
=
"trylock_result"
failed_trylock
=
"failed_trylock"
code
.
putln
(
"int %s = 0;"
%
trylock_result
)
code
.
putln
(
"int %s = 0;"
%
failed_trylock
)
opt_arg_count
=
func_type
.
optional_arg_count
narg_count
=
len
(
func_type
.
args
)
-
opt_arg_count
num_trylock
=
1
op
=
"Cy_TRYRLOCK"
if
reified_function_entry
.
type
.
is_const_method
else
"Cy_TRYWLOCK"
code
.
putln
(
"%s = %s(this->%s) != 0;"
%
(
failed_trylock
,
op
,
target_object_cname
))
code
.
putln
(
"if (!%s) {"
%
failed_trylock
)
code
.
putln
(
"++%s;"
%
trylock_result
)
for
i
,
narg
in
enumerate
(
func_type
.
args
[:
narg_count
]):
if
narg
.
type
.
is_cyp_class
:
try_op
=
"Cy_TRYRLOCK"
if
narg
.
type
.
is_const
else
"Cy_TRYWLOCK"
code
.
putln
(
"%s = %s(this->%s) != 0;"
%
(
failed_trylock
,
try_op
,
narg
.
cname
))
code
.
putln
(
"if (!%s) {"
%
failed_trylock
)
code
.
putln
(
"++%s;"
%
trylock_result
)
num_trylock
+=
1
num_optional_if
=
0
if
opt_arg_count
:
opt_arg_guard
=
code
.
insertion_point
()
code
.
increase_indent
()
for
opt_idx
,
optarg
in
enumerate
(
func_type
.
args
[
narg_count
:]):
if
optarg
.
type
.
is_cyp_class
:
try_op
=
"Cy_TRYRLOCK"
if
optarg
.
type
.
is_const
else
"Cy_TRYWLOCK"
code
.
putln
(
"if (this->%s->%sn > %s) {"
%
(
opt_arg_name
,
Naming
.
pyrex_prefix
,
opt_idx
,
))
code
.
putln
(
"%s = %s(this->%s->%s) != 0;"
%
(
failed_trylock
,
try_op
,
opt_arg_name
,
func_type
.
opt_arg_cname
(
optarg
.
name
)
))
code
.
putln
(
"if (!%s) {"
%
failed_trylock
)
code
.
putln
(
"++%s;"
%
trylock_result
)
num_optional_if
+=
1
num_trylock
+=
1
for
_
in
range
(
num_optional_if
):
code
.
putln
(
"}"
)
if
num_optional_if
>
0
:
opt_arg_guard
.
putln
(
"if (this->%s != NULL) {"
%
opt_arg_name
)
code
.
putln
(
"}"
)
# The check for optional_args != NULL
else
:
code
.
decrease_indent
()
for
_
in
range
(
num_trylock
):
code
.
putln
(
"}"
)
if
num_trylock
:
# If there is any lock failure, we unlock all and return 0
code
.
putln
(
"if (%s) {"
%
failed_trylock
)
num_unlock
=
0
# Target object first, then arguments
code
.
putln
(
"if (%s > %s) {"
%
(
trylock_result
,
num_unlock
))
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
target_object_cname
)
num_unlock
+=
1
for
i
,
narg
in
enumerate
(
func_type
.
args
[:
narg_count
]):
if
narg
.
type
.
is_cyp_class
:
code
.
putln
(
"if (%s > %s) {"
%
(
trylock_result
,
num_unlock
))
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
narg
.
cname
)
num_unlock
+=
1
if
opt_arg_count
and
num_optional_if
:
code
.
putln
(
"if (this->%s != NULL) {"
%
opt_arg_name
)
for
opt_idx
,
optarg
in
enumerate
(
func_type
.
args
[
narg_count
:]):
if
optarg
.
type
.
is_cyp_class
:
code
.
putln
(
"if (%s > %s) {"
%
(
trylock_result
,
num_unlock
))
code
.
putln
(
"Cy_UNLOCK(this->%s->%s);"
%
(
opt_arg_name
,
func_type
.
opt_arg_cname
(
optarg
.
name
)))
num_unlock
+=
1
# Note: we do not respect the semantic order of end-blocks here for simplification purpose.
# This one is for the "not NULL opt arg" check
code
.
putln
(
"}"
)
# These ones are all the checks for mandatory and optional arguments
for
_
in
range
(
num_unlock
):
code
.
putln
(
"}"
)
code
.
putln
(
"return 0;"
)
code
.
putln
(
"}"
)
does_return
=
reified_function_entry
.
type
.
return_type
is
not
PyrexTypes
.
c_void_type
if
does_return
:
result_assignment
=
"%s = "
%
reified_function_entry
.
type
.
return_type
.
declaration_code
(
"result"
)
code
.
putln
(
"%sthis->%s->%s(%s);"
%
(
result_assignment
,
target_object_cname
,
reified_function_entry
.
cname
,
", "
.
join
(
"this->%s"
%
arg_cname
for
arg_cname
in
reified_call_args_list
)
)
)
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
target_object_cname
)
put_cypclass_op_on_narg_optarg
(
lambda
_
:
"Cy_UNLOCK"
,
reified_function_entry
.
type
,
Naming
.
optional_args_cname
,
code
)
code
.
putln
(
"/* Push result in the result object */"
)
if
does_return
:
code
.
putln
(
"Cy_WLOCK(this->%s);"
%
result_attr_cname
)
if
reified_function_entry
.
type
.
return_type
is
PyrexTypes
.
c_int_type
:
code
.
putln
(
"this->%s->pushIntResult(result);"
%
result_attr_cname
)
else
:
code
.
putln
(
"this->%s->pushVoidStarResult((void*)result);"
%
result_attr_cname
)
code
.
putln
(
"Cy_UNLOCK(this->%s);"
%
result_attr_cname
)
code
.
putln
(
"return 1;"
)
code
.
putln
(
"}"
)
# Destructor
code
.
putln
(
"virtual ~%s() {"
%
class_name
)
code
.
putln
(
"Cy_DECREF(this->%s);"
%
target_object_cname
)
put_cypclass_op_on_narg_optarg
(
lambda
_
:
"Cy_DECREF"
,
reified_function_entry
.
type
,
Naming
.
optional_args_cname
,
code
)
if
opt_arg_count
:
code
.
putln
(
"free(this->%s);"
%
Naming
.
optional_args_cname
)
code
.
putln
(
"}"
)
code
.
putln
(
"};"
)
def
generate_cyp_class_wrapper_definition
(
self
,
type
,
wrapper_entry
,
constructor_entry
,
new_entry
,
alloc_entry
,
code
):
"""
Generate cypclass constructor wrapper ? -> TODO what does this do exactly ?
"""
if
type
.
templates
:
code
.
putln
(
"template <typename %s>"
%
", class "
.
join
(
[
T
.
empty_declaration_code
()
for
T
in
type
.
templates
]))
init_entry
=
constructor_entry
self_type
=
wrapper_entry
.
type
.
return_type
.
declaration_code
(
''
)
type_string
=
type
.
empty_declaration_code
()
class_name
=
type
.
name
wrapper_cname
=
"%s::%s__constructor__%s"
%
(
type_string
,
Naming
.
func_prefix
,
class_name
)
wrapper_type
=
wrapper_entry
.
type
arg_decls
=
[]
arg_names
=
[]
for
arg
in
wrapper_type
.
args
[:
len
(
wrapper_type
.
args
)
-
wrapper_type
.
optional_arg_count
]:
arg_decl
=
arg
.
declaration_code
()
arg_decls
.
append
(
arg_decl
)
arg_names
.
append
(
arg
.
cname
)
if
wrapper_type
.
optional_arg_count
:
arg_decls
.
append
(
wrapper_type
.
op_arg_struct
.
declaration_code
(
Naming
.
optional_args_cname
))
arg_names
.
append
(
Naming
.
optional_args_cname
)
if
wrapper_type
.
has_varargs
:
# We can't safely handle varargs because we need
# to know where the size argument is to start a va_list
error
(
wrapper_entry
.
pos
,
"Cypclass cannot handle variable arguments constructors, but you can use optional arguments (arg=some_value)"
)
if
not
arg_decls
:
arg_decls
=
[
"void"
]
decl_arg_string
=
', '
.
join
(
arg_decls
)
code
.
putln
(
"%s %s(%s)"
%
(
self_type
,
wrapper_cname
,
decl_arg_string
))
code
.
putln
(
"{"
)
wrapper_arg_types
=
[
arg
.
type
for
arg
in
wrapper_entry
.
type
.
args
]
pos
=
wrapper_entry
.
pos
or
type
.
entry
.
pos
if
new_entry
:
alloc_type
=
alloc_entry
.
type
new_arg_types
=
[
alloc_type
]
+
wrapper_arg_types
new_entry
=
PyrexTypes
.
best_match
(
new_arg_types
,
new_entry
.
all_alternatives
(),
pos
)
if
new_entry
:
alloc_call_string
=
"("
+
new_entry
.
type
.
original_alloc_type
.
type
.
declaration_code
(
""
)
+
") %s"
%
alloc_entry
.
func_cname
new_arg_names
=
[
alloc_call_string
]
+
arg_names
new_arg_string
=
', '
.
join
(
new_arg_names
)
code
.
putln
(
"%s self =(%s) %s(%s);"
%
(
self_type
,
self_type
,
new_entry
.
func_cname
,
new_arg_string
))
else
:
code
.
putln
(
"%s self = %s();"
%
(
self_type
,
alloc_entry
.
func_cname
))
# __new__ can be defined by user and return another type
is_new_return_type
=
not
new_entry
or
new_entry
.
type
.
return_type
==
type
# allocate and initialise PyObject fields
if
is_new_return_type
and
type
.
wrapper_type
:
self
.
generate_cypclass_wrapper_allocation
(
code
,
type
.
wrapper_type
)
if
init_entry
:
init_entry
=
PyrexTypes
.
best_match
(
wrapper_arg_types
,
init_entry
.
all_alternatives
(),
None
)
if
init_entry
and
(
is_new_return_type
):
# Calling __init__
max_init_nargs
=
len
(
init_entry
.
type
.
args
)
min_init_nargs
=
max_init_nargs
-
init_entry
.
type
.
optional_arg_count
max_wrapper_nargs
=
len
(
wrapper_entry
.
type
.
args
)
min_wrapper_nargs
=
max_wrapper_nargs
-
wrapper_entry
.
type
.
optional_arg_count
if
min_init_nargs
==
min_wrapper_nargs
:
# The optional arguments begin at the same rank for both function
# => just pass the wrapper opt args structure, and everything will be fine.
if
max_wrapper_nargs
>
min_wrapper_nargs
:
# The wrapper has optional args
arg_names
[
-
1
]
=
"(%s) %s"
%
(
init_entry
.
type
.
op_arg_struct
.
declaration_code
(
''
),
arg_names
[
-
1
])
elif
max_init_nargs
>
min_init_nargs
:
# The wrapper has no optional args but the __init__ function does
arg_names
.
append
(
"(%s) NULL"
%
init_entry
.
type
.
op_arg_struct
.
declaration_code
(
''
))
# else, neither __init__ nor __new__ have optional arguments, nothing to do
elif
min_wrapper_nargs
<
min_init_nargs
:
# It means some args from the wrapper should be at
# their default values, which we cannot know from here,
# so shout and stop, sadly.
error
(
init_entry
.
pos
,
"Could not call this __init__ function because the corresponding __new__ wrapper isn't aware of default values"
)
error
(
wrapper_entry
.
pos
,
"Wrapped __new__ is here (some args passed to __init__ could be at their default values)"
)
elif
min_wrapper_nargs
>
min_init_nargs
:
# Here, the __init__ optional arguments start before
# the __new__ ones. We have to unpack the __new__ opt args struct
# in some variables and then repack in the __init__ opt args struct.
init_opt_args_name_list
=
[
arg
.
cname
for
arg
in
wrapper_entry
.
type
.
args
[
min_init_nargs
:]]
# The first __init__ optional arguments are mandatory
# in the __new__ signature, so they will always appear
# in the __init__ optional arguments structure
init_opt_args_number
=
"init_opt_n"
code
.
putln
(
"int %s = %s;"
%
(
init_opt_args_number
,
min_wrapper_nargs
-
min_init_nargs
))
if
wrapper_entry
.
type
.
optional_arg_count
:
for
i
,
arg
in
enumerate
(
wrapper_entry
.
type
.
args
[
min_wrapper_nargs
:]):
# It's an opt arg => it's not declared in the (c++) function scope => declare a variable for it
arg_name
=
arg
.
cname
code
.
putln
(
"%s;"
%
arg
.
type
.
declaration_code
(
arg_name
))
# Arguments unpacking
optional_struct_name
=
arg_names
.
pop
()
code
.
putln
(
"if (%s) {"
%
optional_struct_name
)
# This is necessary to keep __init__ informed of
# how many optional arguments were explicitely given
code
.
putln
(
"%s += %s->%sn;"
%
(
init_opt_args_number
,
optional_struct_name
,
Naming
.
pyrex_prefix
))
braces_number
=
1
+
max_wrapper_nargs
-
min_wrapper_nargs
for
i
,
arg
in
enumerate
(
wrapper_entry
.
type
.
args
[
min_wrapper_nargs
:]):
code
.
putln
(
"if(%s->%sn > %s) {"
%
(
optional_struct_name
,
Naming
.
pyrex_prefix
,
i
))
code
.
putln
(
"%s = %s->%s;"
%
(
arg
.
cname
,
optional_struct_name
,
wrapper_entry
.
type
.
op_arg_struct
.
base_type
.
scope
.
var_entries
[
i
+
1
].
cname
))
for
_
in
range
(
braces_number
):
code
.
putln
(
'}'
)
# Arguments packing
init_opt_args_struct_name
=
"init_opt_args"
code
.
putln
(
"%s;"
%
init_entry
.
type
.
op_arg_struct
.
base_type
.
declaration_code
(
init_opt_args_struct_name
))
code
.
putln
(
"%s.%sn = %s;"
%
(
init_opt_args_struct_name
,
Naming
.
pyrex_prefix
,
init_opt_args_number
))
for
i
,
arg_name
in
enumerate
(
init_opt_args_name_list
):
# The second tuple member is a bit tricky.
# Actually, the only way we have to precisely know the attribute cname
# which corresponds to the argument in the opt args struct
# is to rely on the declaration order in the struct scope.
# FuncDefNode doesn't do this because it has it's declarator node,
# which is not our case here.
code
.
putln
(
"%s.%s = %s;"
%
(
init_opt_args_struct_name
,
init_entry
.
type
.
opt_arg_cname
(
init_entry
.
type
.
args
[
min_init_nargs
+
i
].
name
),
arg_name
))
arg_names
=
arg_names
[:
min_init_nargs
]
+
[
"&"
+
init_opt_args_struct_name
]
init_arg_string
=
','
.
join
(
arg_names
)
code
.
putln
(
"self->%s(%s);"
%
(
init_entry
.
cname
,
init_arg_string
))
code
.
putln
(
"return self;"
)
code
.
putln
(
"}"
)
def
generate_cypclass_wrapper_allocation
(
self
,
code
,
wrapper_type
):
"""
Generate allocation and essential setup of the wrapper object.
The cname of the cyobject is assumed to be 'self'.
The cname 'wrapper' is assumed to be available.
"""
objstruct_cname
=
wrapper_type
.
objstruct_cname
code
.
putln
(
"if (self) {"
)
code
.
putln
(
"%s * wrapper = (%s *) self;"
%
(
objstruct_cname
,
objstruct_cname
))
code
.
putln
(
"Py_REFCNT(wrapper) = 0;"
)
code
.
putln
(
"Py_TYPE(wrapper) = %s;"
%
wrapper_type
.
typeptr_cname
)
code
.
putln
(
"}"
)
def
generate_typedef
(
self
,
entry
,
code
):
base_type
=
entry
.
type
.
typedef_base_type
enclosing_scope
=
entry
.
scope
...
...
@@ -1601,7 +2197,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# for cyp wrappers, just allocate the cyobject and return the wrapper
# let the wrapped __init__ handle initialisation
if
type
.
is_cyp_wrapper
:
from
.CypclassWrapper
import
generate_cypclass_wrapper_allocation
code
.
putln
(
"if (t != %s) {"
%
type
.
typeptr_cname
)
code
.
putln
(
...
...
@@ -1611,7 +2206,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code
.
putln
(
"return NULL;"
)
code
.
putln
(
"}"
)
code
.
putln
(
"CyObject * self = %s();"
%
type
.
wrapped_alloc
)
generate_cypclass_wrapper_allocation
(
code
,
type
)
self
.
generate_cypclass_wrapper_allocation
(
code
,
type
)
code
.
putln
(
"PyObject* wrapper = reinterpret_cast<PyObject *>(static_cast<%s *>(self));"
%
Naming
.
cypclass_wrapper_layout_type
...
...
Cython/Compiler/Pipeline.py
View file @
6094ac85
...
...
@@ -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
.Cypclass
Wrapper
import
CypclassWrapperInjection
from
.Cypclass
Transforms
import
CypclassWrapperInjection
from
.ParseTreeTransforms
import
WithTransform
,
NormalizeTree
,
PostParse
,
PxdPostParse
from
.ParseTreeTransforms
import
ForwardDeclareTypes
,
InjectGilHandling
,
AnalyseDeclarationsTransform
from
.ParseTreeTransforms
import
AnalyseExpressionsTransform
,
FindInvalidUseOfFusedTypes
...
...
Cython/Compiler/Symtab.py
View file @
6094ac85
...
...
@@ -513,6 +513,18 @@ class Scope(object):
for
scope
in
sorted
(
self
.
subscopes
,
key
=
operator
.
attrgetter
(
'scope_prefix'
)):
yield
scope
def
iter_cypclass_entries_and_scopes
(
self
):
"""
Recursively iterate over nested cypclasses and their associated scope
"""
for
entry
in
self
.
cypclass_entries
:
cypclass_scope
=
entry
.
type
.
scope
yield
entry
,
cypclass_scope
if
cypclass_scope
:
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.
...
...
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