# HG changeset patch # User Paul Boddie # Date 1693612124 -7200 # Node ID 23edd8f55d402bc56042402c78fef8d586678b7e # Parent cfdcae64b86f7c4c335da1a7789fe6a5b86fbe04 Allowed arbitrary attributes to be used as result targets. Changed attribute access instructions to obtain attribute references or addresses, employing these when storing attribute values and when specifying result targets. diff -r cfdcae64b86f -r 23edd8f55d40 deducer.py --- a/deducer.py Sat Sep 02 01:19:52 2023 +0200 +++ b/deducer.py Sat Sep 02 01:48:44 2023 +0200 @@ -3,7 +3,7 @@ """ Deduce types for usage observations. -Copyright (C) 2014, 2015, 2016, 2017, 2018 Paul Boddie +Copyright (C) 2014-2018, 2023 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 @@ -2749,39 +2749,56 @@ if access_first_attribute: + # Access via the accessor's class. + if first_method == "relative-class": if assigning: - emit(("__store_via_class", accessor, attrname, "")) + emit(("", ("__get_class_attr_ref", accessor, attrname))) + emit(("__store_via_attr_ref", "", "")) else: accessor = ("__load_via_class", accessor, attrname) + # Access via the accessor itself. + elif first_method == "relative-object": if assigning: - emit(("__store_via_object", accessor, attrname, "")) + emit(("", ("__get_object_attr_ref", accessor, attrname))) + emit(("__store_via_attr_ref", "", "")) else: accessor = ("__load_via_object", accessor, attrname) + # Access via a class accessor or the accessor's class. + elif first_method == "relative-object-class": if assigning: - emit(("__get_class_and_store", accessor, attrname, "")) + emit(("__raise_type_error",)) else: accessor = ("__get_class_and_load", accessor, attrname) + # Access via the accessor's class. + elif first_method == "check-class": if assigning: - emit(("__check_and_store_via_class", accessor, attrname, "")) + emit(("__raise_type_error",)) else: accessor = ("__check_and_load_via_class", accessor, attrname) + # Access via the accessor itself. + elif first_method == "check-object": if assigning: - emit(("__check_and_store_via_object", accessor, attrname, "")) + emit(("", ("__check_and_get_object_attr_ref", accessor, attrname))) + emit(("__store_via_attr_ref", "", "")) else: accessor = ("__check_and_load_via_object", accessor, attrname) + # Access via a class accessor or the accessor's class. + # Here, only access via the accessor is supported. + elif first_method == "check-object-class": if assigning: - emit(("__check_and_store_via_any", accessor, attrname, "")) + emit(("", ("__check_and_get_object_attr_ref", accessor, attrname))) + emit(("__store_via_attr_ref", "", "")) else: accessor = ("__check_and_load_via_any", accessor, attrname) @@ -2815,12 +2832,14 @@ if traversal_mode == "class": if assigning: - emit(("__store_via_class", accessor, attrname, "")) + emit(("", ("__get_class_attr_ref", accessor, attrname))) + emit(("__store_via_attr_ref", "", "")) else: accessor = ("__load_via_class", accessor, attrname) else: if assigning: - emit(("__store_via_object", accessor, attrname, "")) + emit(("", ("__get_object_attr_ref", accessor, attrname))) + emit(("__store_via_attr_ref", "", "")) else: accessor = ("__load_via_object", accessor, attrname) @@ -2855,10 +2874,11 @@ # Constrain instructions involving certain special # attribute names. - to_search = attrname == "__data__" and "object" or "any" + to_search = attrname != "__data__" and "any" or "object" if assigning: - emit(("__check_and_store_via_%s" % to_search, accessor, attrname, "")) + emit(("", ("__check_and_get_object_attr_ref", accessor, attrname))) + emit(("__store_via_attr_ref", "", "")) else: accessor = ("__check_and_load_via_%s" % to_search, accessor, attrname) @@ -2879,7 +2899,8 @@ if final_method == "static-assign": parent, attrname = origin.rsplit(".", 1) - emit(("__store_via_object", parent, attrname, "")) + emit(("", ("__get_object_attr_ref", parent, attrname))) + emit(("__store_via_attr_ref", "", "")) # Invoked attributes employ a separate context. diff -r cfdcae64b86f -r 23edd8f55d40 encoders.py --- a/encoders.py Sat Sep 02 01:19:52 2023 +0200 +++ b/encoders.py Sat Sep 02 01:48:44 2023 +0200 @@ -233,18 +233,12 @@ attribute_loading_ops = ( "__load_via_class", "__load_via_object", "__get_class_and_load", - ) - -attribute_ops = attribute_loading_ops + ( - "__store_via_class", "__store_via_object", - ) - -checked_loading_ops = ( "__check_and_load_via_class", "__check_and_load_via_object", "__check_and_load_via_any", ) -checked_ops = checked_loading_ops + ( - "__check_and_store_via_class", "__check_and_store_via_object", "__check_and_store_via_any", +attribute_ref_lookup_ops = ( + "__get_object_attr_ref", "__get_class_attr_ref", + "__check_and_get_object_attr_ref", ) typename_ops = ( @@ -267,6 +261,14 @@ "", "", ) +attribute_ref_values = ( + "", + ) + +attribute_ref_ops = ( + "", "", + ) + context_values = ( "", ) @@ -279,14 +281,15 @@ "", "", ) -reference_acting_ops = attribute_ops + checked_ops + type_ops + typename_ops -attribute_producing_ops = attribute_loading_ops + checked_loading_ops +reference_acting_ops = attribute_ref_lookup_ops + attribute_loading_ops + type_ops + typename_ops +attribute_producing_ops = attribute_loading_ops attribute_producing_variables = ( "", "", "", "", "" ) -def encode_access_instruction(instruction, subs, accessor_index, context_index): +def encode_access_instruction(instruction, subs, accessor_index, context_index, + attribute_ref_index): """ Encode the 'instruction' - a sequence starting with an operation and @@ -302,6 +305,9 @@ The 'context_index' parameter defines the position in local context storage for the referenced context or affected by a context operation. + The 'attribute_ref_index' parameter defines the position in local attribute + reference storage for a referenced attribute. + Return both the encoded instruction and a collection of substituted names. """ @@ -315,7 +321,8 @@ if args: converting_op = op for arg in args: - s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, accessor_index, context_index) + s, _substituted = encode_access_instruction_arg(arg, subs, + converting_op, accessor_index, context_index, attribute_ref_index) substituted.update(_substituted) a.append(s) converting_op = None @@ -342,6 +349,11 @@ if op in accessor_ops: a.insert(0, accessor_index) + # Add attribute reference storage information to certain operations. + + if op in attribute_ref_ops: + a.insert(0, attribute_ref_index) + # Add context storage information to certain operations. if op in context_ops: @@ -381,7 +393,7 @@ return "%s%s" % (op, argstr), substituted -def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index): +def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index, attribute_ref_index): """ Encode 'arg' using 'subs' to define substitutions, 'op' to indicate the @@ -393,7 +405,8 @@ """ if isinstance(arg, tuple): - encoded, substituted = encode_access_instruction(arg, subs, accessor_index, context_index) + encoded, substituted = encode_access_instruction(arg, subs, + accessor_index, context_index, attribute_ref_index) return attribute_to_reference(op, arg[0], encoded, substituted) # Special values only need replacing, not encoding. @@ -404,6 +417,8 @@ if arg in accessor_values or arg in context_values: encoded = "%s(%s)" % (subs.get(arg), context_index) + elif arg in attribute_ref_values: + encoded = "%s(%s)" % (subs.get(arg), attribute_ref_index) else: encoded = subs.get(arg) diff -r cfdcae64b86f -r 23edd8f55d40 templates/native/common.c --- a/templates/native/common.c Sat Sep 02 01:19:52 2023 +0200 +++ b/templates/native/common.c Sat Sep 02 01:48:44 2023 +0200 @@ -30,9 +30,9 @@ { /* Create a new string and mutate the __data__, __size__ and __key__ attributes. */ __attr attr = __NEWINSTANCE(__builtins___str_str); - __store_via_object(__VALUE(attr), __data__, (__attr) {.strvalue=s}); - __store_via_object(__VALUE(attr), __size__, __INTVALUE(size)); - __store_via_object(__VALUE(attr), __key__, __NULL); + __store_via_attr_ref(__get_object_attr_ref(__VALUE(attr), __data__), (__attr) {.strvalue=s}); + __store_via_attr_ref(__get_object_attr_ref(__VALUE(attr), __size__), __INTVALUE(size)); + __store_via_attr_ref(__get_object_attr_ref(__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); - __store_via_object(__VALUE(attr), __data__, (__attr) {.seqvalue=f}); + __store_via_attr_ref(__get_object_attr_ref(__VALUE(attr), __data__), (__attr) {.seqvalue=f}); return attr; } diff -r cfdcae64b86f -r 23edd8f55d40 templates/native/list.c --- a/templates/native/list.c Sat Sep 02 01:19:52 2023 +0200 +++ b/templates/native/list.c Sat Sep 02 01:48:44 2023 +0200 @@ -56,7 +56,7 @@ /* Replace the __data__ attribute if appropriate. */ if (newdata != data) - __store_via_object(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata})); + __store_via_attr_ref(__get_object_attr_ref(__VALUE(self), __data__), ((__attr) {.seqvalue=newdata})); return __builtins___none_None; } @@ -84,7 +84,7 @@ /* Replace the __data__ attribute if appropriate. */ if (newdata != data) - __store_via_object(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata})); + __store_via_attr_ref(__get_object_attr_ref(__VALUE(self), __data__), ((__attr) {.seqvalue=newdata})); return __builtins___none_None; } diff -r cfdcae64b86f -r 23edd8f55d40 templates/ops.c --- a/templates/ops.c Sat Sep 02 01:19:52 2023 +0200 +++ b/templates/ops.c Sat Sep 02 01:48:44 2023 +0200 @@ -57,11 +57,18 @@ return __test_context(context, __ATTRVALUE(obj)); } +/* Direct retrieval operations, returning attribute locations. */ + +__attr *__get_class_attr_ref__(__ref obj, int pos) +{ + return __get_object_attr_ref__(__get_class(obj), pos); +} + /* Direct retrieval operations, returning and setting attributes. */ __attr __load_via_object__(__ref obj, int pos) { - return obj->attrs[pos]; + return __load_via_attr_ref(__get_object_attr_ref__(obj, pos)); } __attr __load_via_class__(__ref obj, int pos) @@ -77,27 +84,6 @@ return __load_via_object__(obj, pos); } -/* Direct storage operations. */ - -int __store_via_object__(__ref obj, int pos, __attr value) -{ - obj->attrs[pos] = value; - return 1; -} - -int __store_via_class__(__ref obj, int pos, __attr value) -{ - return __store_via_object__(__get_class(obj), pos, value); -} - -int __get_class_and_store__(__ref obj, int pos, __attr value) -{ - /* Forbid class-relative assignments. */ - - __raise_type_error(); - return 0; -} - /* Introspection. */ int __is_instance(__ref obj) @@ -162,14 +148,36 @@ return __HASATTR(obj, pos, code) ? obj : 0; } +/* Attribute testing and location operations, returning the address of the + attribute as opposed to its value. */ + +__attr *__check_and_get_object_attr_ref_null(__ref obj, int pos, int code) +{ + if (__HASATTR(obj, pos, code)) + return __get_object_attr_ref__(obj, pos); + else + return NULL; +} + +__attr *__check_and_get_object_attr_ref__(__ref obj, int pos, int code) +{ + if (__HASATTR(obj, pos, code)) + return __get_object_attr_ref__(obj, pos); + + __raise_type_error(); + return NULL; +} + /* Attribute testing and retrieval operations. */ __attr __check_and_load_via_object_null(__ref obj, int pos, int code) { - if (__HASATTR(obj, pos, code)) - return __load_via_object__(obj, pos); + __attr *attr = __check_and_get_object_attr_ref_null(obj, pos, code); + + if (attr == NULL) + return __NULL; else - return __NULL; + return __load_via_attr_ref(attr); } __attr __check_and_load_via_class__(__ref obj, int pos, int code) @@ -179,54 +187,22 @@ __attr __check_and_load_via_object__(__ref obj, int pos, int code) { - if (__HASATTR(obj, pos, code)) - return __load_via_object__(obj, pos); + __attr attr = __check_and_load_via_object_null(obj, pos, code); - __raise_type_error(); - return __NULL; + if (__ISNULL(attr)) + __raise_type_error(); + + return attr; } __attr __check_and_load_via_any__(__ref obj, int pos, int code) { - __attr out = __check_and_load_via_object_null(obj, pos, code); - if (__ISNULL(out)) - out = __check_and_load_via_class__(obj, pos, code); - return out; -} - -/* Attribute testing and storage operations. */ - -int __check_and_store_via_class__(__ref obj, int pos, int code, __attr value) -{ - /* Forbid class-relative assignments. */ - - __raise_type_error(); - return 0; -} + __attr attr = __check_and_load_via_object_null(obj, pos, code); -int __check_and_store_via_object__(__ref obj, int pos, int code, __attr value) -{ - if (__HASATTR(obj, pos, code)) - { - __store_via_object__(obj, pos, value); - return 1; - } - - /* No suitable attribute. */ + if (__ISNULL(attr)) + attr = __check_and_load_via_class__(obj, pos, code); - __raise_type_error(); - return 0; -} - -int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value) -{ - if (__check_and_store_via_object__(obj, pos, code, value)) - return 1; - - /* Forbid class-relative assignments. */ - - __raise_type_error(); - return 0; + return attr; } /* Context-related operations. */ @@ -448,11 +424,11 @@ return copy; } -/* Store a suitable attribute in a local. For locals that support value - replacement, a copied object is assigned when initialising the local. +/* Store an attribute in a target location. For targets that support value + replacement, a copied object is assigned when initialising the target. NOTE: Only floats are currently supported for value replacement. */ -__attr __set_local(volatile __attr *local, __attr attr) +__attr __set_attr(volatile __attr *target, __attr attr) { __ref obj; @@ -471,6 +447,6 @@ /* Set and return the attribute. */ - *local = attr; + *target = attr; return attr; } diff -r cfdcae64b86f -r 23edd8f55d40 templates/ops.h --- a/templates/ops.h Sat Sep 02 01:19:52 2023 +0200 +++ b/templates/ops.h Sat Sep 02 01:48:44 2023 +0200 @@ -32,6 +32,20 @@ __attr __load_static_replace(__attr context, __ref obj); __attr __load_static_test(__attr context, __ref obj); +/* Direct retrieval operations, returning attribute locations. */ + +__attr *__get_class_attr_ref__(__ref obj, int pos); + +#define __get_object_attr_ref__(OBJ, POS) (&(OBJ)->attrs[POS]) + +#define __get_object_attr_ref(OBJ, ATTRNAME) (__get_object_attr_ref__(OBJ, __ATTRPOS(ATTRNAME))) +#define __get_class_attr_ref(OBJ, ATTRNAME) (__get_class_attr_ref__(OBJ, __ATTRPOS(ATTRNAME))) + +/* Attribute location operations. */ + +#define __load_via_attr_ref(ATTR) (*(ATTR)) +#define __store_via_attr_ref(ATTR, VALUE) __set_attr(ATTR, VALUE) + /* Direct retrieval operations, returning attributes. */ __attr __load_via_class__(__ref obj, int pos); @@ -42,16 +56,6 @@ #define __load_via_object(OBJ, ATTRNAME) (__load_via_object__(OBJ, __ATTRPOS(ATTRNAME))) #define __get_class_and_load(OBJ, ATTRNAME) (__get_class_and_load__(OBJ, __ATTRPOS(ATTRNAME))) -/* Direct storage operations. */ - -int __store_via_class__(__ref obj, int pos, __attr value); -int __store_via_object__(__ref obj, int pos, __attr value); -int __get_class_and_store__(__ref obj, int pos, __attr value); - -#define __store_via_class(OBJ, ATTRNAME, VALUE) (__store_via_class__(OBJ, __ATTRPOS(ATTRNAME), VALUE)) -#define __store_via_object(OBJ, ATTRNAME, VALUE) (__store_via_object__(OBJ, __ATTRPOS(ATTRNAME), VALUE)) -#define __get_class_and_store(OBJ, ATTRNAME, VALUE) (__get_class_and_store__(OBJ, __ATTRPOS(ATTRNAME), VALUE)) - /* Introspection. */ int __is_instance(__ref obj); @@ -77,6 +81,15 @@ #define __test_common_object(OBJ, TYPENAME) (__test_common_object__(OBJ, __ATTRPOS(TYPENAME), __ATTRCODE(TYPENAME))) #define __test_common_type(OBJ, TYPENAME) (__test_common_type__(OBJ, __ATTRPOS(TYPENAME), __ATTRCODE(TYPENAME))) +/* Attribute testing and location operations, returning the address of the + attribute as opposed to its value. */ + +__attr *__check_and_get_object_attr_ref_null(__ref obj, int pos, int code); + +__attr *__check_and_get_object_attr_ref__(__ref obj, int pos, int code); + +#define __check_and_get_object_attr_ref(OBJ, ATTRNAME) (__check_and_get_object_attr_ref__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME))) + /* Attribute testing and retrieval operations. */ __attr __check_and_load_via_object_null(__ref obj, int pos, int code); @@ -89,16 +102,6 @@ #define __check_and_load_via_object(OBJ, ATTRNAME) (__check_and_load_via_object__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME))) #define __check_and_load_via_any(OBJ, ATTRNAME) (__check_and_load_via_any__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME))) -/* Attribute testing and storage operations. */ - -int __check_and_store_via_class__(__ref obj, int pos, int code, __attr value); -int __check_and_store_via_object__(__ref obj, int pos, int code, __attr value); -int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value); - -#define __check_and_store_via_class(OBJ, ATTRNAME, VALUE) (__check_and_store_via_class__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE)) -#define __check_and_store_via_object(OBJ, ATTRNAME, VALUE) (__check_and_store_via_object__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE)) -#define __check_and_store_via_any(OBJ, ATTRNAME, VALUE) (__check_and_store_via_any__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE)) - /* Context-related operations. */ int __test_context_update(__attr context, __attr attr, int invoke); @@ -108,11 +111,13 @@ __attr __test_context_static(int target, __attr context, __ref value, __attr contexts[]); #define __get_accessor(__TARGET) (__tmp_values[__TARGET]) +#define __get_attr_ref(__TARGET) (__tmp_attr_refs[__TARGET]) #define __get_context(__TARGET) (__tmp_contexts[__TARGET]) #define __set_context(__TARGET, __ATTR) (__tmp_contexts[__TARGET] = (__ATTR)) #define __set_private_context(__ATTR) (__tmp_private_context = (__ATTR)) #define __set_accessor(__TARGET, __ATTR) (__tmp_values[__TARGET] = (__ATTR)) #define __set_target_accessor(__ATTR) (__tmp_target_value = (__ATTR)) +#define __set_attr_ref(__TARGET, __ATTR) (__tmp_attr_refs[__TARGET] = (__ATTR)) /* Context testing for invocations. */ @@ -155,6 +160,6 @@ /* Result target administration for potential value replacement. */ -__attr __set_local(volatile __attr *local, __attr attr); +__attr __set_attr(volatile __attr *target, __attr attr); #endif /* __OPS_H__ */ diff -r cfdcae64b86f -r 23edd8f55d40 templates/progops.c --- a/templates/progops.c Sat Sep 02 01:19:52 2023 +0200 +++ b/templates/progops.c Sat Sep 02 01:48:44 2023 +0200 @@ -32,7 +32,7 @@ { obj->table = table; obj->pos = __INSTANCEPOS; - __store_via_object(obj, __class__, __ATTRVALUE(cls)); + __store_via_attr_ref(__get_object_attr_ref(obj, __class__), __ATTRVALUE(cls)); } __attr __new(const __table * table, __ref cls, size_t size, int immutable) @@ -81,7 +81,7 @@ /* Store a reference to the data in the object's __data__ attribute. */ - __store_via_object(__VALUE(self), __data__, (__attr) {.seqvalue=data}); + __store_via_attr_ref(__get_object_attr_ref(__VALUE(self), __data__), (__attr) {.seqvalue=data}); __newdata_sequence(number, data, args); return self; } @@ -97,7 +97,7 @@ /* Store a reference to the data in the object's __data__ attribute. */ - __store_via_object(__VALUE(self), __data__, (__attr) {.seqvalue=data}); + __store_via_attr_ref(__get_object_attr_ref(__VALUE(self), __data__), (__attr) {.seqvalue=data}); __newdata_sequence(number, data, args); return self; } @@ -315,7 +315,7 @@ void __SETDEFAULT(__ref obj, int pos, __attr value) { - __store_via_object__(obj, __FUNCTION_INSTANCE_SIZE + pos, value); + __store_via_attr_ref(__get_object_attr_ref__(obj, __FUNCTION_INSTANCE_SIZE + pos), value); } __attr __GETDEFAULT(__ref obj, int pos) diff -r cfdcae64b86f -r 23edd8f55d40 tests/assign_attr.py --- a/tests/assign_attr.py Sat Sep 02 01:19:52 2023 +0200 +++ b/tests/assign_attr.py Sat Sep 02 01:48:44 2023 +0200 @@ -20,7 +20,7 @@ c.x.x = 2 print c # C(C(2)) print c.x # C(2) -print c.x.x +print c.x.x # 2 print D.x # 1 D.x = 2 @@ -33,3 +33,9 @@ f().x = 5 print D.x # 5 + +D.x = c.x.x +print D.x # 2 + +c.x = D.x +print c.x # 2 diff -r cfdcae64b86f -r 23edd8f55d40 translator.py --- a/translator.py Sat Sep 02 01:19:52 2023 +0200 +++ b/translator.py Sat Sep 02 01:48:44 2023 +0200 @@ -332,8 +332,6 @@ # Handle processing requests on nodes. else: - self.reset_temp_counters() - l = CommonModule.process_structure(self, node) # Return indications of return statement usage. @@ -343,6 +341,13 @@ else: return None + def process_statement_node(self, node): + + "Process the given statement 'node'." + + self.reset_temp_counters() + return CommonModule.process_statement_node(self, node) + def process_structure_node(self, n): "Process the individual node 'n'." @@ -501,6 +506,7 @@ elif isinstance(n, compiler.ast.AssAttr): in_assignment = self.in_assignment + self.result_target_name = "*__get_attr_ref(%d)" % self.attribute_ref_index self.in_assignment = self.process_structure_node(expr) self.statement(self.process_attribute_access(n)) self.in_assignment = in_assignment @@ -556,12 +562,14 @@ # invocation. accessor_index = self.accessor_index + attribute_ref_index = self.attribute_ref_index context_index = self.context_index context_identity = None context_identity_verified = False final_identity = None accessor_test = False accessor_stored = False + attribute_ref_stored = False # Obtain encoded versions of each instruction, accumulating temporary # variables. @@ -573,7 +581,8 @@ if instruction[0] in ("", ""): context_identity, _substituted = \ encode_access_instruction_arg(instruction[1], subs, instruction[0], - accessor_index, context_index) + accessor_index, context_index, + attribute_ref_index) context_identity_verified = instruction[0] == "" continue @@ -595,11 +604,18 @@ elif instruction[0] == "": accessor_stored = True + # Intercept attribute reference storage. + + elif instruction[0] == "": + attribute_ref_stored = True + self.next_attribute_ref() + # Collect the encoded instruction, noting any temporary variables # required by it. encoded, _substituted = encode_access_instruction(instruction, subs, - accessor_index, context_index) + accessor_index, context_index, + attribute_ref_index) output.append(encoded) substituted.update(_substituted) @@ -617,7 +633,7 @@ del self.attrs[0] return AttrResult(output, refs, location, context_identity, context_identity_verified, - accessor_test, accessor_stored) + accessor_test, accessor_stored, attribute_ref_stored) def init_substitutions(self): @@ -637,23 +653,27 @@ # Mappings to be replaced by those given below. "" : "__tmp_values", + "" : "__tmp_attr_refs", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_contexts", "" : "__tmp_private_context", "" : "__tmp_values", + "" : "__tmp_attr_refs", "" : "__tmp_target_value", } self.op_subs = { "" : "__get_accessor", + "" : "__get_attr_ref", "" : "__get_context", "" : "__test_context_revert", "" : "__test_context_static", "" : "__set_context", "" : "__set_private_context", "" : "__set_accessor", + "" : "__set_attr_ref", "" : "__set_target_accessor", } @@ -804,7 +824,7 @@ if ref and not ref.static(): parent, attrname = path.rsplit(".", 1) - self.writestmt("__store_via_object(&%s, %s, __load_via_object(&%s, %s));" % ( + self.writestmt("__store_via_attr_ref(__get_object_attr_ref(&%s, %s), __load_via_object(&%s, %s));" % ( encode_path(class_name), name, encode_path(parent), attrname )) @@ -851,19 +871,15 @@ in_conditional = self.in_conditional self.in_conditional = False - # Reset temporary storage counters and limits. The counters are reset - # for every statement, but are also involved in expressions processed - # for this node. + # Reset temporary storage counters and limits. self.reset_temp_limits() - self.reset_temp_counters() # Reset result target storage details. To be effective, storage # locations are not reused between statements. self.max_result_target = 0 self.result_target = 0 - self.result_target_name = None # Volatile locals for exception handling. @@ -885,6 +901,11 @@ if self.in_method(): for name in self.importer.function_attr_initialisers.get(function_name) or []: + + # Treat each assignment as a separate statement. + + self.reset_temp_counters() + self.process_assignment_node( compiler.ast.AssAttr(compiler.ast.Name("self"), name, "OP_ASSIGN"), compiler.ast.Name(name)) @@ -1297,7 +1318,7 @@ self.record_temp("__tmp_results") self.next_result() else: - result_target = "__NULL" + result_target = None # Arguments are presented in a temporary frame array with any context # always being the first argument. Where it would be unused, it may be @@ -1313,7 +1334,7 @@ # Start with result target and context arguments for each invocation. - args = [result_target, context_arg] + args = [result_target or "__NULL", context_arg] reserved_args = 2 # Complete the array with null values, permitting tests for a complete @@ -1347,8 +1368,8 @@ # Convert any attributes indicating value replacement. - if isinstance(argexpr, InvocationResult): - argexprstr = "__set_local(&%s, %s)" % (argexpr.result_target, argexpr) + if isinstance(argexpr, InvocationResult) and argexpr.result_target: + argexprstr = "__set_attr(&%s, %s)" % (argexpr.result_target, argexpr) else: argexprstr = str(argexpr) @@ -1528,6 +1549,8 @@ self.function_target = 0 self.context_index = 0 self.accessor_index = 0 + self.attribute_ref_index = 0 + self.result_target_name = None def reset_temp_limits(self): @@ -1536,6 +1559,7 @@ self.max_function_target = 0 self.max_context_index = 0 self.max_accessor_index = 0 + self.max_attribute_ref_index = 0 def next_result(self): @@ -1565,6 +1589,13 @@ self.accessor_index += 1 self.max_accessor_index = max(self.accessor_index, self.max_accessor_index) + def next_attribute_ref(self): + + "Allocate the next attribute reference value storage." + + self.attribute_ref_index += 1 + self.max_attribute_ref_index = max(self.attribute_ref_index, self.max_attribute_ref_index) + def always_callable(self, refs): "Determine whether all 'refs' are callable." @@ -1724,12 +1755,13 @@ if expr and self.in_function and not is_global and self.in_try_except: self.make_volatile(n.name) - # Set the replacement target for local objects. - # Note that this will not apply to all local objects, with only floats - # supporting replaceable values. - - if expr and self.in_function and not is_global: - self.result_target_name = encode_path(n.name) + # Set the replacement target. Note that this will not apply to all + # objects, with only floats supporting replaceable values. + + if expr: + target_ref = TrResolvedNameRef(n.name, ref, is_global=is_global, + location=location) + self.result_target_name = str(target_ref) # Expression processing is deferred until after any result target has # been set. @@ -1743,6 +1775,7 @@ name_ref = TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global, location=location) + return not expr and self.get_aliases(name_ref) or name_ref def get_aliases(self, name_ref): @@ -2246,7 +2279,8 @@ if self.uses_temp(name, "__tmp_values"): self.writeline("__attr __tmp_values[%d];" % self.max_accessor_index) - # Add temporary variable usage details. + if self.uses_temp(name, "__tmp_attr_refs"): + self.writeline("__attr *__tmp_attr_refs[%d];" % self.max_attribute_ref_index) if self.uses_temp(name, "__tmp_results"): self.writeline("__attr __tmp_results[%d] = {0};" % self.max_result_target) diff -r cfdcae64b86f -r 23edd8f55d40 transresults.py --- a/transresults.py Sat Sep 02 01:19:52 2023 +0200 +++ b/transresults.py Sat Sep 02 01:48:44 2023 +0200 @@ -109,14 +109,14 @@ # Qualified names must be converted into parent-relative assignments. elif self.parent: - return "__store_via_object(&%s, %s, %s)" % ( + return "__store_via_attr_ref(__get_object_attr_ref(&%s, %s), %s)" % ( encode_path(self.parent), self.attrname, self.expr) # All other assignments involve the names as they were given. # To support value replacement, a special operation is used. else: - return "__set_local(&%s, %s)" % (self.attrname, self.expr) + return "__set_attr(&%s, %s)" % (self.attrname, self.expr) # Expressions. @@ -179,7 +179,8 @@ "A translation result for an attribute access." def __init__(self, instructions, refs, location, context_identity, - context_identity_verified, accessor_test, accessor_stored): + context_identity_verified, accessor_test, accessor_stored, + attribute_ref_stored): InstructionSequence.__init__(self, instructions) self.refs = refs @@ -188,6 +189,7 @@ self.context_identity_verified = context_identity_verified self.accessor_test = accessor_test self.accessor_stored = accessor_stored + self.attribute_ref_stored = attribute_ref_stored def references(self): return self.refs @@ -207,6 +209,9 @@ def stores_accessor(self): return self.accessor_stored + def stores_attribute_ref(self): + return self.attribute_ref_stored + def get_origin(self): return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() @@ -225,10 +230,11 @@ return encode_instructions(self.instructions) def __repr__(self): - return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % ( + return "AttrResult(%r, %r, %r, %r, %r, %r, %r, %r)" % ( self.instructions, self.refs, self.location, self.context_identity, self.context_identity_verified, - self.accessor_test, self.accessor_stored) + self.accessor_test, self.accessor_stored, + self.attribute_ref_stored) class AliasResult(NameRef, Result):