Commit 9562d461 authored by Xavier Thompson's avatar Xavier Thompson

Move Cypclass generating methods to CypclassWrapper.py

parent 00a9a806
......@@ -24,6 +24,40 @@
#
from __future__ import absolute_import
import cython
cython.declare(Naming=object, Options=object, PyrexTypes=object, TypeSlots=object,
error=object, warning=object, py_object_type=object, cy_object_type=object, UtilityCode=object,
EncodedString=object, re=object)
from collections import defaultdict
import json
import operator
import os
import re
from .PyrexTypes import CPtrType
from . import Future
from . import Annotate
from . import Code
from . import CypclassWrapper
from . import Naming
from . import Nodes
from . import Options
from . import TypeSlots
from . import PyrexTypes
from . import Pythran
from .Errors import error, warning
from .PyrexTypes import py_object_type, cy_object_type
from ..Utils import open_new_file, replace_suffix, decode_filename, build_hex_version
from .Code import UtilityCode, IncludeCode
from .StringEncoding import EncodedString
from .Pythran import has_np_pythran
def cypclass_iter(scope):
"""
Recursively iterate over nested cypclasses
......@@ -46,3 +80,589 @@ def generate_cypclass_typeobj_declarations(env, code, definition):
code.putln("static PyTypeObject *%s = 0;" % (
entry.type.typeptr_cname))
#
# Cypclass generation, originally authored by Gwenaël Samain, moved here from ModuleNode.py
#
def generate_cyp_class_deferred_definitions(type_entries, code):
"""
Generate all cypclass method definitions, deferred till now
"""
for entry in type_entries:
if entry.type.is_cyp_class:
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
scope = entry.type.scope
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():
if wrapper_entry.used or entry.type.templates:
generate_cyp_class_wrapper_definition(entry.type, wrapper_entry, constructor, new, alloc, code)
# Generate deferred definitions for any nested types
generate_cyp_class_deferred_definitions(scope.sue_entries, 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
"""
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
"""
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))
if init_entry:
init_entry = PyrexTypes.best_match(wrapper_arg_types,
init_entry.all_alternatives(), None)
if init_entry and (not new_entry or new_entry.type.return_type == 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("}")
......@@ -586,7 +586,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type_entries.append(entry)
type_entries = [t for t in type_entries if t not in vtabslot_entries]
self.generate_type_header_code(type_entries, code)
self.generate_cyp_class_deferred_definitions(type_entries, code)
CypclassWrapper.generate_cyp_class_deferred_definitions(type_entries, code)
for entry in vtabslot_list:
self.generate_objstruct_definition(entry.type, code)
self.generate_typeobj_predeclaration(entry, code)
......@@ -818,28 +818,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
elif type.is_extension_type:
self.generate_objstruct_definition(type, code)
def generate_cyp_class_deferred_definitions(self, type_entries, code):
for entry in type_entries:
if entry.type.is_cyp_class:
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
scope = entry.type.scope
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():
if wrapper_entry.used or entry.type.templates:
self.generate_cyp_class_wrapper_definition(entry.type, wrapper_entry, constructor, new, alloc, code)
# Generate deferred definitions for any nested types
self.generate_cyp_class_deferred_definitions(scope.sue_entries, code)
def generate_gcc33_hack(self, env, code):
# Workaround for spurious warning generation in gcc 3.3
code.putln("")
......@@ -1131,535 +1109,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if inherited_methods:
code.putln("")
def generate_cyp_class_attrs_destructor_definition(self, entry, code):
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):
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):
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):
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):
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))
if init_entry:
init_entry = PyrexTypes.best_match(wrapper_arg_types,
init_entry.all_alternatives(), None)
if init_entry and (not new_entry or new_entry.type.return_type == 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_enum_definition(self, entry, code):
code.mark_pos(entry.pos)
type = entry.type
......
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