Commit 6094ac85 authored by Xavier Thompson's avatar Xavier Thompson

Reorganise cypclass wrapper injection code

parent 341223c4
# #
# Code generation for wrapping cypclasses with cclasses # Cypclass transforms
# #
from __future__ import absolute_import from __future__ import absolute_import
import cython 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 collections import defaultdict
from . import CypclassWrapper
from . import Naming from . import Naming
from . import Nodes from . import Nodes
from . import PyrexTypes from . import PyrexTypes
...@@ -18,7 +17,6 @@ from . import ExprNodes ...@@ -18,7 +17,6 @@ from . import ExprNodes
from . import Visitor from . import Visitor
from . import TreeFragment from . import TreeFragment
from .Errors import error, warning
from .StringEncoding import EncodedString from .StringEncoding import EncodedString
from .ParseTreeTransforms import NormalizeTree, InterpretCompilerDirectives, DecoratorTransform, AnalyseDeclarationsTransform from .ParseTreeTransforms import NormalizeTree, InterpretCompilerDirectives, DecoratorTransform, AnalyseDeclarationsTransform
...@@ -446,619 +444,3 @@ def NAME(ARGDECLS): ...@@ -446,619 +444,3 @@ def NAME(ARGDECLS):
return method_wrapper 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("}")
...@@ -673,12 +673,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -673,12 +673,11 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_exttype_vtabptr_declaration(entry, code) self.generate_exttype_vtabptr_declaration(entry, code)
self.generate_exttype_final_methods_declaration(entry, code) self.generate_exttype_final_methods_declaration(entry, code)
from .CypclassWrapper import generate_cyp_class_deferred_definitions
for module in modules: for module in modules:
definition = module is env definition = module is env
code.putln("") code.putln("")
code.putln("/* Deferred definitions for cypclasses */") 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): def generate_declarations_for_modules(self, env, modules, globalstate):
typecode = globalstate['type_declarations'] typecode = globalstate['type_declarations']
...@@ -925,6 +924,603 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -925,6 +924,603 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("typedef struct %s __pyx_gcc33_%s;" % ( code.putln("typedef struct %s __pyx_gcc33_%s;" % (
name, tail)) 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): def generate_typedef(self, entry, code):
base_type = entry.type.typedef_base_type base_type = entry.type.typedef_base_type
enclosing_scope = entry.scope enclosing_scope = entry.scope
...@@ -1601,7 +2197,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1601,7 +2197,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# for cyp wrappers, just allocate the cyobject and return the wrapper # for cyp wrappers, just allocate the cyobject and return the wrapper
# let the wrapped __init__ handle initialisation # let the wrapped __init__ handle initialisation
if type.is_cyp_wrapper: if type.is_cyp_wrapper:
from .CypclassWrapper import generate_cypclass_wrapper_allocation
code.putln("if (t != %s) {" % type.typeptr_cname) code.putln("if (t != %s) {" % type.typeptr_cname)
code.putln( code.putln(
...@@ -1611,7 +2206,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1611,7 +2206,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("return NULL;") code.putln("return NULL;")
code.putln("}") code.putln("}")
code.putln("CyObject * self = %s();" % type.wrapped_alloc) code.putln("CyObject * self = %s();" % type.wrapped_alloc)
generate_cypclass_wrapper_allocation(code, type) self.generate_cypclass_wrapper_allocation(code, type)
code.putln( code.putln(
"PyObject* wrapper = reinterpret_cast<PyObject *>(static_cast<%s *>(self));" "PyObject* wrapper = reinterpret_cast<PyObject *>(static_cast<%s *>(self));"
% Naming.cypclass_wrapper_layout_type % Naming.cypclass_wrapper_layout_type
......
...@@ -141,7 +141,7 @@ def inject_utility_code_stage_factory(context): ...@@ -141,7 +141,7 @@ def inject_utility_code_stage_factory(context):
def create_pipeline(context, mode, exclude_classes=()): def create_pipeline(context, mode, exclude_classes=()):
assert mode in ('pyx', 'py', 'pxd') assert mode in ('pyx', 'py', 'pxd')
from .Visitor import PrintTree from .Visitor import PrintTree
from .CypclassWrapper import CypclassWrapperInjection from .CypclassTransforms import CypclassWrapperInjection
from .ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse from .ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
from .ParseTreeTransforms import ForwardDeclareTypes, InjectGilHandling, AnalyseDeclarationsTransform from .ParseTreeTransforms import ForwardDeclareTypes, InjectGilHandling, AnalyseDeclarationsTransform
from .ParseTreeTransforms import AnalyseExpressionsTransform, FindInvalidUseOfFusedTypes from .ParseTreeTransforms import AnalyseExpressionsTransform, FindInvalidUseOfFusedTypes
......
...@@ -513,6 +513,18 @@ class Scope(object): ...@@ -513,6 +513,18 @@ class Scope(object):
for scope in sorted(self.subscopes, key=operator.attrgetter('scope_prefix')): for scope in sorted(self.subscopes, key=operator.attrgetter('scope_prefix')):
yield scope 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): def declare_tracked(self, entry):
# Keying only with the name is wrong: if we have multiple attributes # Keying only with the name is wrong: if we have multiple attributes
# with the same name in different cypclass, this will conflict. # with the same name in different cypclass, this will conflict.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment