# HG changeset patch # User Paul Boddie # Date 1531412342 -7200 # Node ID 47b5a25fe02fb3ce1f0a09d6d1480881571743e2 # Parent 8b470b16d26d451b541936bc6aed6a4b1e8e29d3# Parent 0687e5d1ed801a4029e58d0b6b3deeb52fd044b0 Merged changes from the default branch. diff -r 8b470b16d26d -r 47b5a25fe02f deducer.py --- a/deducer.py Tue Jul 10 13:28:53 2018 +0200 +++ b/deducer.py Thu Jul 12 18:19:02 2018 +0200 @@ -2938,7 +2938,13 @@ # Produce an advisory instruction regarding the context. if context_var: - if context_test in ("ignore", "replace"): + + # Only verify the context for invocation purposes if a suitable + # test has been performed. + + if context_test in ("ignore", "replace") or \ + final_method in ("access-invoke", "static-invoke"): + emit(("", context_var)) else: emit(("", context_var)) diff -r 8b470b16d26d -r 47b5a25fe02f generator.py --- a/generator.py Tue Jul 10 13:28:53 2018 +0200 +++ b/generator.py Thu Jul 12 18:19:02 2018 +0200 @@ -3,7 +3,7 @@ """ Generate C code from object layouts and other deduced information. -Copyright (C) 2015, 2016, 2017 Paul Boddie +Copyright (C) 2015, 2016, 2017, 2018 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 @@ -204,14 +204,12 @@ # Generate table and structure data. function_instance_attrs = None - objects = self.optimiser.attr_table.items() + objects = self.optimiser.all_attrs.items() objects.sort() self.callables = {} - for ref, indexes in objects: - attrnames = self.get_attribute_names(indexes) - + for ref, attrnames in objects: kind = ref.get_kind() path = ref.get_origin() table_name = encode_tablename(kind, path) @@ -613,8 +611,7 @@ # Obtain the attributes. cls = ref.get_origin() - indexes = self.optimiser.attr_table[ref] - attrnames = self.get_attribute_names(indexes) + attrnames = self.optimiser.all_attrs[ref] attrs = self.get_instance_attributes(cls, attrnames) # Set the data, if provided. @@ -867,22 +864,6 @@ num_parameters = len(parameters) + 1 return num_parameters - (defaults and len(defaults) or 0), num_parameters - def get_attribute_names(self, indexes): - - """ - Given a list of attribute table 'indexes', return a list of attribute - names. - """ - - all_attrnames = self.optimiser.all_attrnames - attrnames = [] - for i in indexes: - if i is None: - attrnames.append(None) - else: - attrnames.append(all_attrnames[i]) - return attrnames - def get_static_attributes(self, kind, name, attrnames): """ @@ -987,17 +968,8 @@ structure of the given 'kind', adding entries to the object 'structure'. """ - # Populate function instance structures for functions. - - if ref.has_kind(""): - origin = self.function_type - structure_ref = Reference("", self.function_type) - - # Otherwise, just populate the appropriate structures. - - else: - origin = ref.get_origin() - structure_ref = ref + structure_ref = self.get_target_structure(ref) + origin = structure_ref.get_origin() for attrname in self.optimiser.structures[structure_ref]: @@ -1129,6 +1101,20 @@ structure.append(self.encode_member(origin, attrname, attr, kind)) + def get_target_structure(self, ref): + + "Return the target structure type and reference for 'ref'." + + # Populate function instance structures for functions. + + if ref.has_kind(""): + return Reference("", self.function_type) + + # Otherwise, just populate the appropriate structures. + + else: + return ref + def encode_member(self, path, name, ref, structure_type): """ diff -r 8b470b16d26d -r 47b5a25fe02f lib/__builtins__/int.py --- a/lib/__builtins__/int.py Tue Jul 10 13:28:53 2018 +0200 +++ b/lib/__builtins__/int.py Thu Jul 12 18:19:02 2018 +0200 @@ -3,7 +3,7 @@ """ Integer objects. -Copyright (C) 2015, 2016, 2017 Paul Boddie +Copyright (C) 2015, 2016, 2017, 2018 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 @@ -19,12 +19,12 @@ this program. If not, see . """ -from __builtins__.operator import _negate from __builtins__.unicode import utf8string from native import get_maxint, get_minint, is_int, \ - int_add, int_and, int_div, int_eq, int_gt, int_lshift, \ - int_lt, int_mod, int_mul, int_ne, int_neg, int_not, \ - int_or, int_pow, int_rshift, int_str, int_sub, int_xor + int_add, int_and, int_div, int_eq, int_ge, int_gt, \ + int_lshift, int_le, int_lt, int_mod, int_mul, int_ne, \ + int_neg, int_not, int_or, int_pow, int_rshift, int_str, \ + int_sub, int_xor class int: @@ -140,6 +140,8 @@ return self._binary_op_rev(int_div, other) + # NOTE: To be implemented. + def __floordiv__(self, other): pass def __rfloordiv__(self, other): pass def __ifloordiv__(self, other): pass @@ -189,7 +191,6 @@ return self._binary_op_rev(int_rshift, other) __ilshift__ = __lshift__ - __irshift__ = __rshift__ def __lt__(self, other): @@ -208,13 +209,13 @@ "Return whether this int is less than or equal to 'other'." - return _negate(self.__gt__(other)) + return self._binary_op(int_le, other) def __ge__(self, other): "Return whether this int is greater than or equal to 'other'." - return _negate(self.__lt__(other)) + return self._binary_op(int_ge, other) def __eq__(self, other): @@ -226,7 +227,7 @@ "Return whether this int is not equal to 'other'." - return _negate(self.__eq__(other)) + return self._binary_op(int_ne, other) def __neg__(self): diff -r 8b470b16d26d -r 47b5a25fe02f optimiser.py --- a/optimiser.py Tue Jul 10 13:28:53 2018 +0200 +++ b/optimiser.py Thu Jul 12 18:19:02 2018 +0200 @@ -3,7 +3,7 @@ """ Optimise object layouts and generate access instruction plans. -Copyright (C) 2014, 2015, 2016, 2017 Paul Boddie +Copyright (C) 2014, 2015, 2016, 2017, 2018 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 @@ -413,6 +413,12 @@ finally: f.close() + def is_allocated_attribute(self, attrname): + + "Return whether 'attrname' is to be allocated in an object." + + return not attrname.startswith("$t") + def populate_objects(self): "Populate objects using attribute and usage information." @@ -432,8 +438,8 @@ # Remove temporary names from structures. - attrnames = filter(lambda x: not x.startswith("$t"), attrnames) - self.all_attrs[(objkind, name)] = attrnames + attrnames = filter(self.is_allocated_attribute, attrnames) + self.all_attrs[Reference(objkind, name)] = attrnames try: self.locations = get_allocated_locations(self.all_attrs, @@ -495,8 +501,7 @@ # Record the structures. - for (objkind, name), attrnames in self.all_attrs.items(): - key = Reference(objkind, name) + for key, attrnames in self.all_attrs.items(): l = self.structures[key] = [None] * len(attrnames) for attrname in attrnames: @@ -719,7 +724,7 @@ """ Get attribute and size information for the object attributes defined by 'd' - providing a mapping from (object kind, type name) to attribute names. + providing a mapping from object references to attribute names. Return a matrix of attributes (each row entry consisting of column values providing attribute names, with value positions corresponding to types @@ -736,15 +741,15 @@ sizes = {} objkinds = {} - for objtype, attrnames in d.items(): - objkind, _name = objtype + for ref, attrnames in d.items(): + objkind = ref.get_kind() for attrname in attrnames: # Record each type supporting the attribute. init_item(attrs, attrname, set) - attrs[attrname].add(objtype) + attrs[attrname].add(ref) # Maintain a record of the smallest object size supporting the given # attribute. @@ -761,13 +766,13 @@ # Obtain attribute details in order of size and occupancy. - all_objtypes = d.keys() + all_refs = d.keys() rsizes = [] for attrname, size in sizes.items(): priority = "" in objkinds[attrname] and 0.5 or 1 occupied = len(attrs[attrname]) - key = (priority * size, size, len(all_objtypes) - occupied, attrname) + key = (priority * size, size, len(all_refs) - occupied, attrname) rsizes.append(key) rsizes.sort() @@ -775,20 +780,20 @@ # Make a matrix of attributes. matrix = {} - for attrname, objtypes in attrs.items(): + for attrname, refs in attrs.items(): # Traverse the object types, adding the attribute name if the object # type supports the attribute, adding None otherwise. row = [] - for objtype in all_objtypes: - if objtype in objtypes: + for ref in all_refs: + if ref in refs: row.append(attrname) else: row.append(None) matrix[attrname] = row - return matrix, all_objtypes, rsizes + return matrix, all_refs, rsizes def get_parameters_and_sizes(d): diff -r 8b470b16d26d -r 47b5a25fe02f templates/native/common.c --- a/templates/native/common.c Tue Jul 10 13:28:53 2018 +0200 +++ b/templates/native/common.c Thu Jul 12 18:19:02 2018 +0200 @@ -1,6 +1,6 @@ /* Common operations for native functions. -Copyright (C) 2016, 2017 Paul Boddie +Copyright (C) 2016, 2017, 2018 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 @@ -30,9 +30,9 @@ { /* Create a new string and mutate the __data__, __size__ and __key__ attributes. */ __attr attr = __NEWINSTANCE(__builtins___str_string); - attr.value->attrs[__ATTRPOS(__data__)].strvalue = s; - attr.value->attrs[__ATTRPOS(__size__)] = __INTVALUE(size); - attr.value->attrs[__ATTRPOS(__key__)] = __NULL; + __store_via_object(__VALUE(attr), __data__, (__attr) {.strvalue=s}); + __store_via_object(__VALUE(attr), __size__, __INTVALUE(size)); + __store_via_object(__VALUE(attr), __key__, __NULL); return attr; } @@ -40,7 +40,7 @@ { /* Create a new list and mutate the __data__ attribute. */ __attr attr = __NEWINSTANCE(__builtins___list_list); - attr.value->attrs[__ATTRPOS(__data__)].seqvalue = f; + __store_via_object(__VALUE(attr), __data__, (__attr) {.seqvalue=f}); return attr; } diff -r 8b470b16d26d -r 47b5a25fe02f templates/ops.c --- a/templates/ops.c Tue Jul 10 13:28:53 2018 +0200 +++ b/templates/ops.c Thu Jul 12 18:19:02 2018 +0200 @@ -1,6 +1,6 @@ /* Common operations. -Copyright (C) 2015, 2016, 2017 Paul Boddie +Copyright (C) 2015, 2016, 2017, 2018 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 @@ -231,7 +231,7 @@ /* Context-related operations. */ -int __test_context_update(__attr context, __attr attr) +int __test_context_update(__attr context, __attr attr, int invoke) { /* Return whether the context should be updated for the attribute. */ @@ -258,6 +258,11 @@ __raise_type_error(); } + /* Without a null or instance context, an invocation cannot be performed. */ + + if (invoke) + __raise_unbound_method_error(); + /* Test for access to a type class attribute using a type instance. */ if (__test_specific_type(attrcontextvalue, &__TYPE_CLASS_TYPE) && __is_type_instance(__VALUE(context))) @@ -272,7 +277,7 @@ { /* Update the context or return the unchanged attribute. */ - if (__test_context_update(context, attr)) + if (__test_context_update(context, attr, 0)) return __update_context(context, attr); else return attr; @@ -288,7 +293,7 @@ /* Revert the local context to that employed by the attribute if the supplied context is not appropriate. */ - if (!__test_context_update(context, attr)) + if (!__test_context_update(context, attr, 1)) contexts[target] = __CONTEXT_AS_VALUE(attr); return attr; } @@ -297,7 +302,7 @@ { /* Set the local context to the specified context if appropriate. */ - if (__test_context_update(context, __ATTRVALUE(value))) + if (__test_context_update(context, __ATTRVALUE(value), 1)) contexts[target] = context; return __ATTRVALUE(value); } diff -r 8b470b16d26d -r 47b5a25fe02f templates/ops.h --- a/templates/ops.h Tue Jul 10 13:28:53 2018 +0200 +++ b/templates/ops.h Thu Jul 12 18:19:02 2018 +0200 @@ -101,7 +101,7 @@ /* Context-related operations. */ -int __test_context_update(__attr context, __attr attr); +int __test_context_update(__attr context, __attr attr, int invoke); __attr __test_context(__attr context, __attr attr); __attr __update_context(__attr context, __attr attr); __attr __test_context_revert(int target, __attr context, __attr attr, __attr contexts[]); diff -r 8b470b16d26d -r 47b5a25fe02f templates/progops.c --- a/templates/progops.c Tue Jul 10 13:28:53 2018 +0200 +++ b/templates/progops.c Thu Jul 12 18:19:02 2018 +0200 @@ -151,6 +151,11 @@ __Raise(__new___builtins___core_OverflowError(__NULL)); } +void __raise_unbound_method_error() +{ + __Raise(__new___builtins___core_UnboundMethodInvocation(__NULL)); +} + void __raise_type_error() { __Raise(__new___builtins___core_TypeError(__NULL)); diff -r 8b470b16d26d -r 47b5a25fe02f templates/progops.h --- a/templates/progops.h Tue Jul 10 13:28:53 2018 +0200 +++ b/templates/progops.h Thu Jul 12 18:19:02 2018 +0200 @@ -1,6 +1,6 @@ /* Operations depending on program specifics. -Copyright (C) 2015, 2016, 2017 Paul Boddie +Copyright (C) 2015, 2016, 2017, 2018 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 @@ -55,6 +55,8 @@ void __raise_overflow_error(); +void __raise_unbound_method_error(); + void __raise_zero_division_error(); void __raise_type_error(); diff -r 8b470b16d26d -r 47b5a25fe02f tests/methods_rebound.py --- a/tests/methods_rebound.py Tue Jul 10 13:28:53 2018 +0200 +++ b/tests/methods_rebound.py Thu Jul 12 18:19:02 2018 +0200 @@ -13,9 +13,32 @@ d = D() +def fn(): + return 456 + +class E: + f = fn + g = C.f + +e = E() + print c.f.__name__ # f print c.f() # <__main__.C instance> # 123 print d.f.__name__ # wrapper print d.f() # <__main__.C instance> # 123 + +print e.f.__name__ # fn +print e.f() # 456 +print e.g.__name__ # f + +try: + print e.g() +except TypeError: + print "e.g(): e is an incompatible instance for E.g which is C.f" + +g = get_using(E.g, c) +print g.__name__ # f +print g() # <__main__.C instance> + # 123 diff -r 8b470b16d26d -r 47b5a25fe02f translator.py --- a/translator.py Tue Jul 10 13:28:53 2018 +0200 +++ b/translator.py Thu Jul 12 18:19:02 2018 +0200 @@ -1070,9 +1070,21 @@ context_required = True have_access_context = isinstance(expr, AttrResult) + + # The context identity is merely the thing providing the context. + # A verified context is one that does not need further testing for + # suitability. + context_identity = have_access_context and expr.context() context_verified = have_access_context and expr.context_verified() + + # The presence of any test operations in the accessor expression. + # With such operations present, the expression cannot be eliminated. + tests_accessor = have_access_context and expr.tests_accessor() + + # Parameter details and parameter list dimensions. + parameters = None num_parameters = None num_defaults = None @@ -1252,11 +1264,13 @@ if context_required: if have_access_context: - args = [context_identity] + context_arg = context_identity else: - args = ["__CONTEXT_AS_VALUE(%s)" % target_var] + context_arg = "__CONTEXT_AS_VALUE(%s)" % target_var else: - args = ["__NULL"] + context_arg = "__NULL" + + args = [context_arg] # Complete the array with null values, permitting tests for a complete # set of arguments. @@ -1398,25 +1412,23 @@ elif function: if context_required: - # With context_verified or context_identity... - - if have_access_context: + # Avoid further context testing if appropriate. + + if have_access_context and context_verified: emit("__get_function_member(%s)" % target_expr) # Otherwise, test the context for the function/method. else: - emit("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % ( - target_var, target_expr)) + emit("__get_function(%s, %s)" % (context_arg, target_expr)) else: emit("_get_function_member(%s)" % target_expr) # With known parameters, the target can be tested. elif known_parameters: - context_arg = context_required and args[0] or "__NULL" if self.always_callable(refs): - if context_verified or context_identity: + if context_verified: emit("__get_function_member(%s)" % target_expr) else: emit("__get_function(%s, %s)" % (context_arg, target_expr))