# HG changeset patch # User Paul Boddie # Date 1485812086 -3600 # Node ID 0c397087e94ca47eb4bd328534855b02e5436606 # Parent 8152446c3bb634a74890083e49da3d2989425155 Eliminated separate objects for bound and unbound methods, requiring that the context be checked when obtaining the function pointer to invoke methods, at least where this cannot be avoided in the code generated for such invocations. diff -r 8152446c3bb6 -r 0c397087e94c encoders.py --- a/encoders.py Mon Jan 30 22:32:38 2017 +0100 +++ b/encoders.py Mon Jan 30 22:34:46 2017 +0100 @@ -306,12 +306,6 @@ else: return encode_path(arg), set() -def encode_bound_reference(path): - - "Encode 'path' as a bound method name." - - return "__bound_%s" % encode_path(path) - def encode_function_pointer(path): "Encode 'path' as a reference to an output program function." diff -r 8152446c3bb6 -r 0c397087e94c generator.py --- a/generator.py Mon Jan 30 22:32:38 2017 +0100 +++ b/generator.py Mon Jan 30 22:34:46 2017 +0100 @@ -20,7 +20,7 @@ """ from common import CommonOutput, get_builtin_module, get_builtin_type -from encoders import encode_bound_reference, encode_function_pointer, \ +from encoders import encode_function_pointer, \ encode_instantiator_pointer, \ encode_literal_constant, encode_literal_constant_member, \ encode_literal_constant_size, encode_literal_constant_value, \ @@ -282,20 +282,15 @@ if parent_kind == "": - # A bound version of a method. + # A method. - structure = self.populate_function(path, function_instance_attrs, False) - self.write_structure(f_decls, f_defs, encode_bound_reference(path), table_name, structure) - - # An unbound version of a method. - - structure = self.populate_function(path, function_instance_attrs, True) + structure = self.populate_function(path, function_instance_attrs) self.write_structure(f_decls, f_defs, path, table_name, structure) else: # A normal function. - structure = self.populate_function(path, function_instance_attrs, False) + structure = self.populate_function(path, function_instance_attrs) self.write_structure(f_decls, f_defs, path, table_name, structure) # Functions with defaults need to declare instance structures. @@ -821,32 +816,27 @@ name, pos = value table.append((encode_symbol("pcode", name), pos)) - def populate_function(self, path, function_instance_attrs, unbound=False): + def populate_function(self, path, function_instance_attrs): """ Populate a structure for the function with the given 'path'. The given - 'attrs' provide the instance attributes, and if 'unbound' is set to a - true value, an unbound method structure is produced (as opposed to a - callable bound method structure). + 'attrs' provide the instance attributes. """ structure = [] - self.populate_structure(Reference("", path), function_instance_attrs, "", structure, unbound) + self.populate_structure(Reference("", path), function_instance_attrs, "", structure) # Append default members. self.append_defaults(path, structure) return structure - def populate_structure(self, ref, attrs, kind, structure, unbound=False): + def populate_structure(self, ref, attrs, kind, structure): """ Traverse the attributes in the determined order for the structure having the given 'ref' whose members are provided by the 'attrs' mapping, in a structure of the given 'kind', adding entries to the object 'structure'. - If 'unbound' is set to a true value, an unbound method function pointer - will be employed, with a reference to the bound method incorporated into - the special __fn__ attribute. """ # Populate function instance structures for functions. @@ -877,29 +867,21 @@ if attrname == "__fn__": - # Provide bound method references and the unbound function - # pointer if populating methods in a class. - - bound_attr = None - - # Classes offer instantiators. + # Classes offer instantiators which can be called without a + # context. if kind == "": attr = encode_instantiator_pointer(attr) - - # Methods offers references to bound versions and an unbound - # method function. + unbound_attr = attr - elif unbound: - bound_attr = encode_bound_reference(attr) - attr = "__unbound_method" - - # Other functions just offer function pointers. + # Provide bound method and unbound function pointers if + # populating methods in a class. else: attr = encode_function_pointer(attr) + unbound_attr = "__unbound_method" - structure.append("{.b=%s, .fn=%s}" % (bound_attr and "&%s" % bound_attr or "0", attr)) + structure.append("{.inv=%s, .fn=%s}" % (unbound_attr, attr)) continue # Special argument specification member. diff -r 8152446c3bb6 -r 0c397087e94c optimiser.py --- a/optimiser.py Mon Jan 30 22:32:38 2017 +0100 +++ b/optimiser.py Mon Jan 30 22:34:46 2017 +0100 @@ -520,18 +520,7 @@ emit(("__test_context", context_var, accessor)) elif context_test == "replace": - - # Static invocation targets have a context added but no other - # transformation performed. - - if final_method == "static-invoke": - emit(("__update_context", context_var, accessor)) - - # Other invocation targets gain a context and have the bound - # version of the callable activated. - - else: - emit(("__replace_context", context_var, accessor)) + emit(("__update_context", context_var, accessor)) elif final_method not in ("assign", "static-assign"): emit(accessor) diff -r 8152446c3bb6 -r 0c397087e94c templates/native/introspection.c --- a/templates/native/introspection.c Mon Jan 30 22:32:38 2017 +0100 +++ b/templates/native/introspection.c Mon Jan 30 22:34:46 2017 +0100 @@ -1,6 +1,6 @@ /* Native functions for introspection operations. -Copyright (C) 2016 Paul Boddie +Copyright (C) 2016, 2017 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -51,7 +51,7 @@ /* Update the context to the object if it is a method. */ - return __replace_context(obj->value, out); + return __update_context(obj->value, out); } return out; diff -r 8152446c3bb6 -r 0c397087e94c templates/ops.c --- a/templates/ops.c Mon Jan 30 22:32:38 2017 +0100 +++ b/templates/ops.c Mon Jan 30 22:34:46 2017 +0100 @@ -1,6 +1,6 @@ /* Common operations. -Copyright (C) 2015, 2016 Paul Boddie +Copyright (C) 2015, 2016, 2017 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -21,6 +21,7 @@ #include "progops.h" /* for raising errors */ #include "progconsts.h" #include "progtypes.h" +#include /* Direct access and manipulation of static objects. */ @@ -202,7 +203,7 @@ if (__is_instance(context)) { if (__test_common_instance(context, __TYPEPOS(attr.context), __TYPECODE(attr.context))) - return __replace_context(context, attr); + return __update_context(context, attr); else __raise_type_error(); } @@ -210,34 +211,58 @@ /* Test for access to a type class attribute using a type instance. */ if (__test_specific_type(attr.context, &__TYPE_CLASS_TYPE) && __is_type_instance(context)) - return __replace_context(context, attr); + return __update_context(context, attr); /* Otherwise, preserve the attribute as retrieved. */ return attr; } -__attr __replace_context(__ref context, __attr attr) -{ - __attr out; - - /* Set the context. */ - - out.context = context; - - /* Reference a callable version of the attribute by obtaining the bound - method reference from the __fn__ special attribute. */ - - out.value = __load_via_object(attr.value, __ATTRPOS(__fn__)).b; - return out; -} - __attr __update_context(__ref context, __attr attr) { __attr out = {.context=context, .value=attr.value}; return out; } +/* Context testing for invocations. */ + +int __type_method_invocation(__attr attr) +{ + __attr parent; + + /* Require instances, not classes, where methods are function instances. */ + + if (!__is_instance(attr.value)) + return 0; + + /* Access the parent of the callable and test if it is the type object. */ + + parent = __check_and_load_via_object_null(attr.value, __ATTRPOS(__parent__), __ATTRCODE(__parent__)); + return ((parent.value != 0) && __test_specific_type(parent.value, &__TYPE_CLASS_TYPE) && __is_type_instance(attr.context)); +} + +__attr (*__get_function(__attr attr))(__attr[]) +{ + /* Require null or instance contexts for functions and methods respectively, + or type instance contexts for type methods. */ + + if ((attr.context == 0) || __is_instance(attr.context) || __type_method_invocation(attr)) + return __load_via_object(attr.value, __ATTRPOS(__fn__)).fn; + else + return __load_via_object(attr.value, __ATTRPOS(__fn__)).inv; +} + +__attr (*__check_and_get_function(__attr attr))(__attr[]) +{ + /* Require null or instance contexts for functions and methods respectively, + or type instance contexts for type methods. */ + + if ((attr.context == 0) || __is_instance(attr.context) || __type_method_invocation(attr)) + return __check_and_load_via_object(attr.value, __ATTRPOS(__fn__), __ATTRCODE(__fn__)).fn; + else + return __check_and_load_via_object(attr.value, __ATTRPOS(__fn__), __ATTRCODE(__fn__)).inv; +} + /* Basic structure tests. */ int __WITHIN(__ref obj, int pos) diff -r 8152446c3bb6 -r 0c397087e94c templates/ops.h --- a/templates/ops.h Mon Jan 30 22:32:38 2017 +0100 +++ b/templates/ops.h Mon Jan 30 22:34:46 2017 +0100 @@ -1,6 +1,6 @@ /* Common operations. -Copyright (C) 2015, 2016 Paul Boddie +Copyright (C) 2015, 2016, 2017 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -69,13 +69,17 @@ /* Context-related operations. */ __attr __test_context(__ref context, __attr attr); -__attr __replace_context(__ref context, __attr attr); __attr __update_context(__ref context, __attr attr); #define __set_context(__ATTR) (__tmp_context = (__ATTR).value) #define __set_accessor(__ATTR) (__tmp_value = (__ATTR).value) #define __set_target_accessor(__ATTR) (__tmp_target_value = (__ATTR).value) +/* Context testing for invocations. */ + +__attr (*__get_function(__attr attr))(__attr[]); +__attr (*__check_and_get_function(__attr attr))(__attr[]); + /* Basic structure tests. */ int __WITHIN(__ref obj, int pos); diff -r 8152446c3bb6 -r 0c397087e94c templates/progops.c --- a/templates/progops.c Mon Jan 30 22:32:38 2017 +0100 +++ b/templates/progops.c Mon Jan 30 22:34:46 2017 +0100 @@ -1,6 +1,6 @@ /* Operations depending on program specifics. -Copyright (C) 2015, 2016 Paul Boddie +Copyright (C) 2015, 2016, 2017 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -243,9 +243,7 @@ /* Call with the prepared arguments. */ - return (always_callable ? __load_via_object(callable.value, __pos___fn__) - : __check_and_load_via_object(callable.value, __pos___fn__, __code___fn__) - ).fn(allargs); + return (always_callable ? __get_function(callable) : __check_and_get_function(callable))(allargs); } /* Error routines. */ diff -r 8152446c3bb6 -r 0c397087e94c templates/types.h --- a/templates/types.h Mon Jan 30 22:32:38 2017 +0100 +++ b/templates/types.h Mon Jan 30 22:34:46 2017 +0100 @@ -1,6 +1,6 @@ /* Runtime types. -Copyright (C) 2015, 2016 Paul Boddie +Copyright (C) 2015, 2016, 2017 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -57,9 +57,9 @@ union { __obj * context; /* attribute context */ - __obj * b; /* bound callable object */ unsigned int min; /* minimum number of parameters */ unsigned int code; /* parameter table code for key */ + struct __attr (*inv)(); /* unbound callable details */ size_t size; /* size of value */ }; diff -r 8152446c3bb6 -r 0c397087e94c translator.py --- a/translator.py Mon Jan 30 22:32:38 2017 +0100 +++ b/translator.py Mon Jan 30 22:34:46 2017 +0100 @@ -22,7 +22,7 @@ from common import CommonModule, CommonOutput, InstructionSequence, \ first, get_builtin_module, get_builtin_type, init_item, \ predefined_constants -from encoders import encode_access_instruction, encode_bound_reference, \ +from encoders import encode_access_instruction, \ encode_function_pointer, encode_literal_constant, \ encode_literal_instantiator, encode_instantiator_pointer, \ encode_instructions, \ @@ -1072,10 +1072,7 @@ # Handle bound methods. if not instance_name: - if self.is_method(objpath): - instance_name = "&%s" % encode_bound_reference(objpath) - else: - instance_name = "&%s" % encode_path(objpath) + instance_name = "&%s" % encode_path(objpath) # Where defaults are involved but cannot be identified, obtain a new # instance of the lambda and populate the defaults. @@ -1164,7 +1161,7 @@ if expr.has_kind(""): instantiation = objpath target = encode_instantiator_pointer(objpath) - target_structure = "&%s" % encode_bound_reference("%s.__init__" % objpath) + target_structure = "&%s" % encode_path("%s.__init__" % objpath) context_required = False # Only plain functions and bound methods employ function pointers. @@ -1188,9 +1185,7 @@ # Access bound method defaults even if it is not clear whether # the accessor is appropriate. - target_structure = self.is_method(objpath) and \ - "&%s" % encode_bound_reference(objpath) or \ - "&%s" % encode_path(objpath) + target_structure = "&%s" % encode_path(objpath) # Other targets are retrieved at run-time. @@ -1309,8 +1304,12 @@ elif function: self.record_temp("__tmp_targets") - stages.append("__load_via_object(__tmp_targets[%d].value, %s).fn" % ( - self.function_target, encode_symbol("pos", "__fn__"))) + + if context_required: + stages.append("__get_function(__tmp_targets[%d])" % self.function_target) + else: + stages.append("__load_via_object(__tmp_targets[%d].value, %s).fn" % ( + self.function_target, encode_symbol("pos", "__fn__"))) # With a known target, the function is obtained directly and called. # By putting the invocation at the end of the final element in the