1.1 --- a/deducer.py Sat Sep 02 01:19:52 2023 +0200
1.2 +++ b/deducer.py Sat Sep 02 01:48:44 2023 +0200
1.3 @@ -3,7 +3,7 @@
1.4 """
1.5 Deduce types for usage observations.
1.6
1.7 -Copyright (C) 2014, 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2014-2018, 2023 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -2749,39 +2749,56 @@
1.13
1.14 if access_first_attribute:
1.15
1.16 + # Access via the accessor's class.
1.17 +
1.18 if first_method == "relative-class":
1.19 if assigning:
1.20 - emit(("__store_via_class", accessor, attrname, "<assexpr>"))
1.21 + emit(("<set_attr_ref>", ("__get_class_attr_ref", accessor, attrname)))
1.22 + emit(("__store_via_attr_ref", "<attr_ref>", "<assexpr>"))
1.23 else:
1.24 accessor = ("__load_via_class", accessor, attrname)
1.25
1.26 + # Access via the accessor itself.
1.27 +
1.28 elif first_method == "relative-object":
1.29 if assigning:
1.30 - emit(("__store_via_object", accessor, attrname, "<assexpr>"))
1.31 + emit(("<set_attr_ref>", ("__get_object_attr_ref", accessor, attrname)))
1.32 + emit(("__store_via_attr_ref", "<attr_ref>", "<assexpr>"))
1.33 else:
1.34 accessor = ("__load_via_object", accessor, attrname)
1.35
1.36 + # Access via a class accessor or the accessor's class.
1.37 +
1.38 elif first_method == "relative-object-class":
1.39 if assigning:
1.40 - emit(("__get_class_and_store", accessor, attrname, "<assexpr>"))
1.41 + emit(("__raise_type_error",))
1.42 else:
1.43 accessor = ("__get_class_and_load", accessor, attrname)
1.44
1.45 + # Access via the accessor's class.
1.46 +
1.47 elif first_method == "check-class":
1.48 if assigning:
1.49 - emit(("__check_and_store_via_class", accessor, attrname, "<assexpr>"))
1.50 + emit(("__raise_type_error",))
1.51 else:
1.52 accessor = ("__check_and_load_via_class", accessor, attrname)
1.53
1.54 + # Access via the accessor itself.
1.55 +
1.56 elif first_method == "check-object":
1.57 if assigning:
1.58 - emit(("__check_and_store_via_object", accessor, attrname, "<assexpr>"))
1.59 + emit(("<set_attr_ref>", ("__check_and_get_object_attr_ref", accessor, attrname)))
1.60 + emit(("__store_via_attr_ref", "<attr_ref>", "<assexpr>"))
1.61 else:
1.62 accessor = ("__check_and_load_via_object", accessor, attrname)
1.63
1.64 + # Access via a class accessor or the accessor's class.
1.65 + # Here, only access via the accessor is supported.
1.66 +
1.67 elif first_method == "check-object-class":
1.68 if assigning:
1.69 - emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>"))
1.70 + emit(("<set_attr_ref>", ("__check_and_get_object_attr_ref", accessor, attrname)))
1.71 + emit(("__store_via_attr_ref", "<attr_ref>", "<assexpr>"))
1.72 else:
1.73 accessor = ("__check_and_load_via_any", accessor, attrname)
1.74
1.75 @@ -2815,12 +2832,14 @@
1.76
1.77 if traversal_mode == "class":
1.78 if assigning:
1.79 - emit(("__store_via_class", accessor, attrname, "<assexpr>"))
1.80 + emit(("<set_attr_ref>", ("__get_class_attr_ref", accessor, attrname)))
1.81 + emit(("__store_via_attr_ref", "<attr_ref>", "<assexpr>"))
1.82 else:
1.83 accessor = ("__load_via_class", accessor, attrname)
1.84 else:
1.85 if assigning:
1.86 - emit(("__store_via_object", accessor, attrname, "<assexpr>"))
1.87 + emit(("<set_attr_ref>", ("__get_object_attr_ref", accessor, attrname)))
1.88 + emit(("__store_via_attr_ref", "<attr_ref>", "<assexpr>"))
1.89 else:
1.90 accessor = ("__load_via_object", accessor, attrname)
1.91
1.92 @@ -2855,10 +2874,11 @@
1.93 # Constrain instructions involving certain special
1.94 # attribute names.
1.95
1.96 - to_search = attrname == "__data__" and "object" or "any"
1.97 + to_search = attrname != "__data__" and "any" or "object"
1.98
1.99 if assigning:
1.100 - emit(("__check_and_store_via_%s" % to_search, accessor, attrname, "<assexpr>"))
1.101 + emit(("<set_attr_ref>", ("__check_and_get_object_attr_ref", accessor, attrname)))
1.102 + emit(("__store_via_attr_ref", "<attr_ref>", "<assexpr>"))
1.103 else:
1.104 accessor = ("__check_and_load_via_%s" % to_search, accessor, attrname)
1.105
1.106 @@ -2879,7 +2899,8 @@
1.107
1.108 if final_method == "static-assign":
1.109 parent, attrname = origin.rsplit(".", 1)
1.110 - emit(("__store_via_object", parent, attrname, "<assexpr>"))
1.111 + emit(("<set_attr_ref>", ("__get_object_attr_ref", parent, attrname)))
1.112 + emit(("__store_via_attr_ref", "<attr_ref>", "<assexpr>"))
1.113
1.114 # Invoked attributes employ a separate context.
1.115
2.1 --- a/encoders.py Sat Sep 02 01:19:52 2023 +0200
2.2 +++ b/encoders.py Sat Sep 02 01:48:44 2023 +0200
2.3 @@ -233,18 +233,12 @@
2.4
2.5 attribute_loading_ops = (
2.6 "__load_via_class", "__load_via_object", "__get_class_and_load",
2.7 - )
2.8 -
2.9 -attribute_ops = attribute_loading_ops + (
2.10 - "__store_via_class", "__store_via_object",
2.11 - )
2.12 -
2.13 -checked_loading_ops = (
2.14 "__check_and_load_via_class", "__check_and_load_via_object", "__check_and_load_via_any",
2.15 )
2.16
2.17 -checked_ops = checked_loading_ops + (
2.18 - "__check_and_store_via_class", "__check_and_store_via_object", "__check_and_store_via_any",
2.19 +attribute_ref_lookup_ops = (
2.20 + "__get_object_attr_ref", "__get_class_attr_ref",
2.21 + "__check_and_get_object_attr_ref",
2.22 )
2.23
2.24 typename_ops = (
2.25 @@ -267,6 +261,14 @@
2.26 "<accessor>", "<set_accessor>",
2.27 )
2.28
2.29 +attribute_ref_values = (
2.30 + "<attr_ref>",
2.31 + )
2.32 +
2.33 +attribute_ref_ops = (
2.34 + "<attr_ref>", "<set_attr_ref>",
2.35 + )
2.36 +
2.37 context_values = (
2.38 "<context>",
2.39 )
2.40 @@ -279,14 +281,15 @@
2.41 "<test_context_revert>", "<test_context_static>",
2.42 )
2.43
2.44 -reference_acting_ops = attribute_ops + checked_ops + type_ops + typename_ops
2.45 -attribute_producing_ops = attribute_loading_ops + checked_loading_ops
2.46 +reference_acting_ops = attribute_ref_lookup_ops + attribute_loading_ops + type_ops + typename_ops
2.47 +attribute_producing_ops = attribute_loading_ops
2.48
2.49 attribute_producing_variables = (
2.50 "<accessor>", "<context>", "<name>", "<private_context>", "<target_accessor>"
2.51 )
2.52
2.53 -def encode_access_instruction(instruction, subs, accessor_index, context_index):
2.54 +def encode_access_instruction(instruction, subs, accessor_index, context_index,
2.55 + attribute_ref_index):
2.56
2.57 """
2.58 Encode the 'instruction' - a sequence starting with an operation and
2.59 @@ -302,6 +305,9 @@
2.60 The 'context_index' parameter defines the position in local context storage
2.61 for the referenced context or affected by a context operation.
2.62
2.63 + The 'attribute_ref_index' parameter defines the position in local attribute
2.64 + reference storage for a referenced attribute.
2.65 +
2.66 Return both the encoded instruction and a collection of substituted names.
2.67 """
2.68
2.69 @@ -315,7 +321,8 @@
2.70 if args:
2.71 converting_op = op
2.72 for arg in args:
2.73 - s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, accessor_index, context_index)
2.74 + s, _substituted = encode_access_instruction_arg(arg, subs,
2.75 + converting_op, accessor_index, context_index, attribute_ref_index)
2.76 substituted.update(_substituted)
2.77 a.append(s)
2.78 converting_op = None
2.79 @@ -342,6 +349,11 @@
2.80 if op in accessor_ops:
2.81 a.insert(0, accessor_index)
2.82
2.83 + # Add attribute reference storage information to certain operations.
2.84 +
2.85 + if op in attribute_ref_ops:
2.86 + a.insert(0, attribute_ref_index)
2.87 +
2.88 # Add context storage information to certain operations.
2.89
2.90 if op in context_ops:
2.91 @@ -381,7 +393,7 @@
2.92
2.93 return "%s%s" % (op, argstr), substituted
2.94
2.95 -def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index):
2.96 +def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index, attribute_ref_index):
2.97
2.98 """
2.99 Encode 'arg' using 'subs' to define substitutions, 'op' to indicate the
2.100 @@ -393,7 +405,8 @@
2.101 """
2.102
2.103 if isinstance(arg, tuple):
2.104 - encoded, substituted = encode_access_instruction(arg, subs, accessor_index, context_index)
2.105 + encoded, substituted = encode_access_instruction(arg, subs,
2.106 + accessor_index, context_index, attribute_ref_index)
2.107 return attribute_to_reference(op, arg[0], encoded, substituted)
2.108
2.109 # Special values only need replacing, not encoding.
2.110 @@ -404,6 +417,8 @@
2.111
2.112 if arg in accessor_values or arg in context_values:
2.113 encoded = "%s(%s)" % (subs.get(arg), context_index)
2.114 + elif arg in attribute_ref_values:
2.115 + encoded = "%s(%s)" % (subs.get(arg), attribute_ref_index)
2.116 else:
2.117 encoded = subs.get(arg)
2.118
3.1 --- a/templates/native/common.c Sat Sep 02 01:19:52 2023 +0200
3.2 +++ b/templates/native/common.c Sat Sep 02 01:48:44 2023 +0200
3.3 @@ -30,9 +30,9 @@
3.4 {
3.5 /* Create a new string and mutate the __data__, __size__ and __key__ attributes. */
3.6 __attr attr = __NEWINSTANCE(__builtins___str_str);
3.7 - __store_via_object(__VALUE(attr), __data__, (__attr) {.strvalue=s});
3.8 - __store_via_object(__VALUE(attr), __size__, __INTVALUE(size));
3.9 - __store_via_object(__VALUE(attr), __key__, __NULL);
3.10 + __store_via_attr_ref(__get_object_attr_ref(__VALUE(attr), __data__), (__attr) {.strvalue=s});
3.11 + __store_via_attr_ref(__get_object_attr_ref(__VALUE(attr), __size__), __INTVALUE(size));
3.12 + __store_via_attr_ref(__get_object_attr_ref(__VALUE(attr), __key__), __NULL);
3.13 return attr;
3.14 }
3.15
3.16 @@ -40,7 +40,7 @@
3.17 {
3.18 /* Create a new list and mutate the __data__ attribute. */
3.19 __attr attr = __NEWINSTANCE(__builtins___list_list);
3.20 - __store_via_object(__VALUE(attr), __data__, (__attr) {.seqvalue=f});
3.21 + __store_via_attr_ref(__get_object_attr_ref(__VALUE(attr), __data__), (__attr) {.seqvalue=f});
3.22 return attr;
3.23 }
3.24
4.1 --- a/templates/native/list.c Sat Sep 02 01:19:52 2023 +0200
4.2 +++ b/templates/native/list.c Sat Sep 02 01:48:44 2023 +0200
4.3 @@ -56,7 +56,7 @@
4.4
4.5 /* Replace the __data__ attribute if appropriate. */
4.6 if (newdata != data)
4.7 - __store_via_object(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata}));
4.8 + __store_via_attr_ref(__get_object_attr_ref(__VALUE(self), __data__), ((__attr) {.seqvalue=newdata}));
4.9 return __builtins___none_None;
4.10 }
4.11
4.12 @@ -84,7 +84,7 @@
4.13
4.14 /* Replace the __data__ attribute if appropriate. */
4.15 if (newdata != data)
4.16 - __store_via_object(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata}));
4.17 + __store_via_attr_ref(__get_object_attr_ref(__VALUE(self), __data__), ((__attr) {.seqvalue=newdata}));
4.18 return __builtins___none_None;
4.19 }
4.20
5.1 --- a/templates/ops.c Sat Sep 02 01:19:52 2023 +0200
5.2 +++ b/templates/ops.c Sat Sep 02 01:48:44 2023 +0200
5.3 @@ -57,11 +57,18 @@
5.4 return __test_context(context, __ATTRVALUE(obj));
5.5 }
5.6
5.7 +/* Direct retrieval operations, returning attribute locations. */
5.8 +
5.9 +__attr *__get_class_attr_ref__(__ref obj, int pos)
5.10 +{
5.11 + return __get_object_attr_ref__(__get_class(obj), pos);
5.12 +}
5.13 +
5.14 /* Direct retrieval operations, returning and setting attributes. */
5.15
5.16 __attr __load_via_object__(__ref obj, int pos)
5.17 {
5.18 - return obj->attrs[pos];
5.19 + return __load_via_attr_ref(__get_object_attr_ref__(obj, pos));
5.20 }
5.21
5.22 __attr __load_via_class__(__ref obj, int pos)
5.23 @@ -77,27 +84,6 @@
5.24 return __load_via_object__(obj, pos);
5.25 }
5.26
5.27 -/* Direct storage operations. */
5.28 -
5.29 -int __store_via_object__(__ref obj, int pos, __attr value)
5.30 -{
5.31 - obj->attrs[pos] = value;
5.32 - return 1;
5.33 -}
5.34 -
5.35 -int __store_via_class__(__ref obj, int pos, __attr value)
5.36 -{
5.37 - return __store_via_object__(__get_class(obj), pos, value);
5.38 -}
5.39 -
5.40 -int __get_class_and_store__(__ref obj, int pos, __attr value)
5.41 -{
5.42 - /* Forbid class-relative assignments. */
5.43 -
5.44 - __raise_type_error();
5.45 - return 0;
5.46 -}
5.47 -
5.48 /* Introspection. */
5.49
5.50 int __is_instance(__ref obj)
5.51 @@ -162,14 +148,36 @@
5.52 return __HASATTR(obj, pos, code) ? obj : 0;
5.53 }
5.54
5.55 +/* Attribute testing and location operations, returning the address of the
5.56 + attribute as opposed to its value. */
5.57 +
5.58 +__attr *__check_and_get_object_attr_ref_null(__ref obj, int pos, int code)
5.59 +{
5.60 + if (__HASATTR(obj, pos, code))
5.61 + return __get_object_attr_ref__(obj, pos);
5.62 + else
5.63 + return NULL;
5.64 +}
5.65 +
5.66 +__attr *__check_and_get_object_attr_ref__(__ref obj, int pos, int code)
5.67 +{
5.68 + if (__HASATTR(obj, pos, code))
5.69 + return __get_object_attr_ref__(obj, pos);
5.70 +
5.71 + __raise_type_error();
5.72 + return NULL;
5.73 +}
5.74 +
5.75 /* Attribute testing and retrieval operations. */
5.76
5.77 __attr __check_and_load_via_object_null(__ref obj, int pos, int code)
5.78 {
5.79 - if (__HASATTR(obj, pos, code))
5.80 - return __load_via_object__(obj, pos);
5.81 + __attr *attr = __check_and_get_object_attr_ref_null(obj, pos, code);
5.82 +
5.83 + if (attr == NULL)
5.84 + return __NULL;
5.85 else
5.86 - return __NULL;
5.87 + return __load_via_attr_ref(attr);
5.88 }
5.89
5.90 __attr __check_and_load_via_class__(__ref obj, int pos, int code)
5.91 @@ -179,54 +187,22 @@
5.92
5.93 __attr __check_and_load_via_object__(__ref obj, int pos, int code)
5.94 {
5.95 - if (__HASATTR(obj, pos, code))
5.96 - return __load_via_object__(obj, pos);
5.97 + __attr attr = __check_and_load_via_object_null(obj, pos, code);
5.98
5.99 - __raise_type_error();
5.100 - return __NULL;
5.101 + if (__ISNULL(attr))
5.102 + __raise_type_error();
5.103 +
5.104 + return attr;
5.105 }
5.106
5.107 __attr __check_and_load_via_any__(__ref obj, int pos, int code)
5.108 {
5.109 - __attr out = __check_and_load_via_object_null(obj, pos, code);
5.110 - if (__ISNULL(out))
5.111 - out = __check_and_load_via_class__(obj, pos, code);
5.112 - return out;
5.113 -}
5.114 -
5.115 -/* Attribute testing and storage operations. */
5.116 -
5.117 -int __check_and_store_via_class__(__ref obj, int pos, int code, __attr value)
5.118 -{
5.119 - /* Forbid class-relative assignments. */
5.120 -
5.121 - __raise_type_error();
5.122 - return 0;
5.123 -}
5.124 + __attr attr = __check_and_load_via_object_null(obj, pos, code);
5.125
5.126 -int __check_and_store_via_object__(__ref obj, int pos, int code, __attr value)
5.127 -{
5.128 - if (__HASATTR(obj, pos, code))
5.129 - {
5.130 - __store_via_object__(obj, pos, value);
5.131 - return 1;
5.132 - }
5.133 -
5.134 - /* No suitable attribute. */
5.135 + if (__ISNULL(attr))
5.136 + attr = __check_and_load_via_class__(obj, pos, code);
5.137
5.138 - __raise_type_error();
5.139 - return 0;
5.140 -}
5.141 -
5.142 -int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value)
5.143 -{
5.144 - if (__check_and_store_via_object__(obj, pos, code, value))
5.145 - return 1;
5.146 -
5.147 - /* Forbid class-relative assignments. */
5.148 -
5.149 - __raise_type_error();
5.150 - return 0;
5.151 + return attr;
5.152 }
5.153
5.154 /* Context-related operations. */
5.155 @@ -448,11 +424,11 @@
5.156 return copy;
5.157 }
5.158
5.159 -/* Store a suitable attribute in a local. For locals that support value
5.160 - replacement, a copied object is assigned when initialising the local.
5.161 +/* Store an attribute in a target location. For targets that support value
5.162 + replacement, a copied object is assigned when initialising the target.
5.163 NOTE: Only floats are currently supported for value replacement. */
5.164
5.165 -__attr __set_local(volatile __attr *local, __attr attr)
5.166 +__attr __set_attr(volatile __attr *target, __attr attr)
5.167 {
5.168 __ref obj;
5.169
5.170 @@ -471,6 +447,6 @@
5.171
5.172 /* Set and return the attribute. */
5.173
5.174 - *local = attr;
5.175 + *target = attr;
5.176 return attr;
5.177 }
6.1 --- a/templates/ops.h Sat Sep 02 01:19:52 2023 +0200
6.2 +++ b/templates/ops.h Sat Sep 02 01:48:44 2023 +0200
6.3 @@ -32,6 +32,20 @@
6.4 __attr __load_static_replace(__attr context, __ref obj);
6.5 __attr __load_static_test(__attr context, __ref obj);
6.6
6.7 +/* Direct retrieval operations, returning attribute locations. */
6.8 +
6.9 +__attr *__get_class_attr_ref__(__ref obj, int pos);
6.10 +
6.11 +#define __get_object_attr_ref__(OBJ, POS) (&(OBJ)->attrs[POS])
6.12 +
6.13 +#define __get_object_attr_ref(OBJ, ATTRNAME) (__get_object_attr_ref__(OBJ, __ATTRPOS(ATTRNAME)))
6.14 +#define __get_class_attr_ref(OBJ, ATTRNAME) (__get_class_attr_ref__(OBJ, __ATTRPOS(ATTRNAME)))
6.15 +
6.16 +/* Attribute location operations. */
6.17 +
6.18 +#define __load_via_attr_ref(ATTR) (*(ATTR))
6.19 +#define __store_via_attr_ref(ATTR, VALUE) __set_attr(ATTR, VALUE)
6.20 +
6.21 /* Direct retrieval operations, returning attributes. */
6.22
6.23 __attr __load_via_class__(__ref obj, int pos);
6.24 @@ -42,16 +56,6 @@
6.25 #define __load_via_object(OBJ, ATTRNAME) (__load_via_object__(OBJ, __ATTRPOS(ATTRNAME)))
6.26 #define __get_class_and_load(OBJ, ATTRNAME) (__get_class_and_load__(OBJ, __ATTRPOS(ATTRNAME)))
6.27
6.28 -/* Direct storage operations. */
6.29 -
6.30 -int __store_via_class__(__ref obj, int pos, __attr value);
6.31 -int __store_via_object__(__ref obj, int pos, __attr value);
6.32 -int __get_class_and_store__(__ref obj, int pos, __attr value);
6.33 -
6.34 -#define __store_via_class(OBJ, ATTRNAME, VALUE) (__store_via_class__(OBJ, __ATTRPOS(ATTRNAME), VALUE))
6.35 -#define __store_via_object(OBJ, ATTRNAME, VALUE) (__store_via_object__(OBJ, __ATTRPOS(ATTRNAME), VALUE))
6.36 -#define __get_class_and_store(OBJ, ATTRNAME, VALUE) (__get_class_and_store__(OBJ, __ATTRPOS(ATTRNAME), VALUE))
6.37 -
6.38 /* Introspection. */
6.39
6.40 int __is_instance(__ref obj);
6.41 @@ -77,6 +81,15 @@
6.42 #define __test_common_object(OBJ, TYPENAME) (__test_common_object__(OBJ, __ATTRPOS(TYPENAME), __ATTRCODE(TYPENAME)))
6.43 #define __test_common_type(OBJ, TYPENAME) (__test_common_type__(OBJ, __ATTRPOS(TYPENAME), __ATTRCODE(TYPENAME)))
6.44
6.45 +/* Attribute testing and location operations, returning the address of the
6.46 + attribute as opposed to its value. */
6.47 +
6.48 +__attr *__check_and_get_object_attr_ref_null(__ref obj, int pos, int code);
6.49 +
6.50 +__attr *__check_and_get_object_attr_ref__(__ref obj, int pos, int code);
6.51 +
6.52 +#define __check_and_get_object_attr_ref(OBJ, ATTRNAME) (__check_and_get_object_attr_ref__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME)))
6.53 +
6.54 /* Attribute testing and retrieval operations. */
6.55
6.56 __attr __check_and_load_via_object_null(__ref obj, int pos, int code);
6.57 @@ -89,16 +102,6 @@
6.58 #define __check_and_load_via_object(OBJ, ATTRNAME) (__check_and_load_via_object__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME)))
6.59 #define __check_and_load_via_any(OBJ, ATTRNAME) (__check_and_load_via_any__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME)))
6.60
6.61 -/* Attribute testing and storage operations. */
6.62 -
6.63 -int __check_and_store_via_class__(__ref obj, int pos, int code, __attr value);
6.64 -int __check_and_store_via_object__(__ref obj, int pos, int code, __attr value);
6.65 -int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value);
6.66 -
6.67 -#define __check_and_store_via_class(OBJ, ATTRNAME, VALUE) (__check_and_store_via_class__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE))
6.68 -#define __check_and_store_via_object(OBJ, ATTRNAME, VALUE) (__check_and_store_via_object__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE))
6.69 -#define __check_and_store_via_any(OBJ, ATTRNAME, VALUE) (__check_and_store_via_any__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE))
6.70 -
6.71 /* Context-related operations. */
6.72
6.73 int __test_context_update(__attr context, __attr attr, int invoke);
6.74 @@ -108,11 +111,13 @@
6.75 __attr __test_context_static(int target, __attr context, __ref value, __attr contexts[]);
6.76
6.77 #define __get_accessor(__TARGET) (__tmp_values[__TARGET])
6.78 +#define __get_attr_ref(__TARGET) (__tmp_attr_refs[__TARGET])
6.79 #define __get_context(__TARGET) (__tmp_contexts[__TARGET])
6.80 #define __set_context(__TARGET, __ATTR) (__tmp_contexts[__TARGET] = (__ATTR))
6.81 #define __set_private_context(__ATTR) (__tmp_private_context = (__ATTR))
6.82 #define __set_accessor(__TARGET, __ATTR) (__tmp_values[__TARGET] = (__ATTR))
6.83 #define __set_target_accessor(__ATTR) (__tmp_target_value = (__ATTR))
6.84 +#define __set_attr_ref(__TARGET, __ATTR) (__tmp_attr_refs[__TARGET] = (__ATTR))
6.85
6.86 /* Context testing for invocations. */
6.87
6.88 @@ -155,6 +160,6 @@
6.89
6.90 /* Result target administration for potential value replacement. */
6.91
6.92 -__attr __set_local(volatile __attr *local, __attr attr);
6.93 +__attr __set_attr(volatile __attr *target, __attr attr);
6.94
6.95 #endif /* __OPS_H__ */
7.1 --- a/templates/progops.c Sat Sep 02 01:19:52 2023 +0200
7.2 +++ b/templates/progops.c Sat Sep 02 01:48:44 2023 +0200
7.3 @@ -32,7 +32,7 @@
7.4 {
7.5 obj->table = table;
7.6 obj->pos = __INSTANCEPOS;
7.7 - __store_via_object(obj, __class__, __ATTRVALUE(cls));
7.8 + __store_via_attr_ref(__get_object_attr_ref(obj, __class__), __ATTRVALUE(cls));
7.9 }
7.10
7.11 __attr __new(const __table * table, __ref cls, size_t size, int immutable)
7.12 @@ -81,7 +81,7 @@
7.13
7.14 /* Store a reference to the data in the object's __data__ attribute. */
7.15
7.16 - __store_via_object(__VALUE(self), __data__, (__attr) {.seqvalue=data});
7.17 + __store_via_attr_ref(__get_object_attr_ref(__VALUE(self), __data__), (__attr) {.seqvalue=data});
7.18 __newdata_sequence(number, data, args);
7.19 return self;
7.20 }
7.21 @@ -97,7 +97,7 @@
7.22
7.23 /* Store a reference to the data in the object's __data__ attribute. */
7.24
7.25 - __store_via_object(__VALUE(self), __data__, (__attr) {.seqvalue=data});
7.26 + __store_via_attr_ref(__get_object_attr_ref(__VALUE(self), __data__), (__attr) {.seqvalue=data});
7.27 __newdata_sequence(number, data, args);
7.28 return self;
7.29 }
7.30 @@ -315,7 +315,7 @@
7.31
7.32 void __SETDEFAULT(__ref obj, int pos, __attr value)
7.33 {
7.34 - __store_via_object__(obj, __FUNCTION_INSTANCE_SIZE + pos, value);
7.35 + __store_via_attr_ref(__get_object_attr_ref__(obj, __FUNCTION_INSTANCE_SIZE + pos), value);
7.36 }
7.37
7.38 __attr __GETDEFAULT(__ref obj, int pos)
8.1 --- a/tests/assign_attr.py Sat Sep 02 01:19:52 2023 +0200
8.2 +++ b/tests/assign_attr.py Sat Sep 02 01:48:44 2023 +0200
8.3 @@ -20,7 +20,7 @@
8.4 c.x.x = 2
8.5 print c # C(C(2))
8.6 print c.x # C(2)
8.7 -print c.x.x
8.8 +print c.x.x # 2
8.9
8.10 print D.x # 1
8.11 D.x = 2
8.12 @@ -33,3 +33,9 @@
8.13
8.14 f().x = 5
8.15 print D.x # 5
8.16 +
8.17 +D.x = c.x.x
8.18 +print D.x # 2
8.19 +
8.20 +c.x = D.x
8.21 +print c.x # 2
9.1 --- a/translator.py Sat Sep 02 01:19:52 2023 +0200
9.2 +++ b/translator.py Sat Sep 02 01:48:44 2023 +0200
9.3 @@ -332,8 +332,6 @@
9.4 # Handle processing requests on nodes.
9.5
9.6 else:
9.7 - self.reset_temp_counters()
9.8 -
9.9 l = CommonModule.process_structure(self, node)
9.10
9.11 # Return indications of return statement usage.
9.12 @@ -343,6 +341,13 @@
9.13 else:
9.14 return None
9.15
9.16 + def process_statement_node(self, node):
9.17 +
9.18 + "Process the given statement 'node'."
9.19 +
9.20 + self.reset_temp_counters()
9.21 + return CommonModule.process_statement_node(self, node)
9.22 +
9.23 def process_structure_node(self, n):
9.24
9.25 "Process the individual node 'n'."
9.26 @@ -501,6 +506,7 @@
9.27
9.28 elif isinstance(n, compiler.ast.AssAttr):
9.29 in_assignment = self.in_assignment
9.30 + self.result_target_name = "*__get_attr_ref(%d)" % self.attribute_ref_index
9.31 self.in_assignment = self.process_structure_node(expr)
9.32 self.statement(self.process_attribute_access(n))
9.33 self.in_assignment = in_assignment
9.34 @@ -556,12 +562,14 @@
9.35 # invocation.
9.36
9.37 accessor_index = self.accessor_index
9.38 + attribute_ref_index = self.attribute_ref_index
9.39 context_index = self.context_index
9.40 context_identity = None
9.41 context_identity_verified = False
9.42 final_identity = None
9.43 accessor_test = False
9.44 accessor_stored = False
9.45 + attribute_ref_stored = False
9.46
9.47 # Obtain encoded versions of each instruction, accumulating temporary
9.48 # variables.
9.49 @@ -573,7 +581,8 @@
9.50 if instruction[0] in ("<context_identity>", "<context_identity_verified>"):
9.51 context_identity, _substituted = \
9.52 encode_access_instruction_arg(instruction[1], subs, instruction[0],
9.53 - accessor_index, context_index)
9.54 + accessor_index, context_index,
9.55 + attribute_ref_index)
9.56 context_identity_verified = instruction[0] == "<context_identity_verified>"
9.57 continue
9.58
9.59 @@ -595,11 +604,18 @@
9.60 elif instruction[0] == "<set_accessor>":
9.61 accessor_stored = True
9.62
9.63 + # Intercept attribute reference storage.
9.64 +
9.65 + elif instruction[0] == "<set_attr_ref>":
9.66 + attribute_ref_stored = True
9.67 + self.next_attribute_ref()
9.68 +
9.69 # Collect the encoded instruction, noting any temporary variables
9.70 # required by it.
9.71
9.72 encoded, _substituted = encode_access_instruction(instruction, subs,
9.73 - accessor_index, context_index)
9.74 + accessor_index, context_index,
9.75 + attribute_ref_index)
9.76 output.append(encoded)
9.77 substituted.update(_substituted)
9.78
9.79 @@ -617,7 +633,7 @@
9.80 del self.attrs[0]
9.81 return AttrResult(output, refs, location,
9.82 context_identity, context_identity_verified,
9.83 - accessor_test, accessor_stored)
9.84 + accessor_test, accessor_stored, attribute_ref_stored)
9.85
9.86 def init_substitutions(self):
9.87
9.88 @@ -637,23 +653,27 @@
9.89 # Mappings to be replaced by those given below.
9.90
9.91 "<accessor>" : "__tmp_values",
9.92 + "<attr_ref>" : "__tmp_attr_refs",
9.93 "<context>" : "__tmp_contexts",
9.94 "<test_context_revert>" : "__tmp_contexts",
9.95 "<test_context_static>" : "__tmp_contexts",
9.96 "<set_context>" : "__tmp_contexts",
9.97 "<set_private_context>" : "__tmp_private_context",
9.98 "<set_accessor>" : "__tmp_values",
9.99 + "<set_attr_ref>" : "__tmp_attr_refs",
9.100 "<set_target_accessor>" : "__tmp_target_value",
9.101 }
9.102
9.103 self.op_subs = {
9.104 "<accessor>" : "__get_accessor",
9.105 + "<attr_ref>" : "__get_attr_ref",
9.106 "<context>" : "__get_context",
9.107 "<test_context_revert>" : "__test_context_revert",
9.108 "<test_context_static>" : "__test_context_static",
9.109 "<set_context>" : "__set_context",
9.110 "<set_private_context>" : "__set_private_context",
9.111 "<set_accessor>" : "__set_accessor",
9.112 + "<set_attr_ref>" : "__set_attr_ref",
9.113 "<set_target_accessor>" : "__set_target_accessor",
9.114 }
9.115
9.116 @@ -804,7 +824,7 @@
9.117 if ref and not ref.static():
9.118 parent, attrname = path.rsplit(".", 1)
9.119
9.120 - self.writestmt("__store_via_object(&%s, %s, __load_via_object(&%s, %s));" % (
9.121 + self.writestmt("__store_via_attr_ref(__get_object_attr_ref(&%s, %s), __load_via_object(&%s, %s));" % (
9.122 encode_path(class_name), name,
9.123 encode_path(parent), attrname
9.124 ))
9.125 @@ -851,19 +871,15 @@
9.126 in_conditional = self.in_conditional
9.127 self.in_conditional = False
9.128
9.129 - # Reset temporary storage counters and limits. The counters are reset
9.130 - # for every statement, but are also involved in expressions processed
9.131 - # for this node.
9.132 + # Reset temporary storage counters and limits.
9.133
9.134 self.reset_temp_limits()
9.135 - self.reset_temp_counters()
9.136
9.137 # Reset result target storage details. To be effective, storage
9.138 # locations are not reused between statements.
9.139
9.140 self.max_result_target = 0
9.141 self.result_target = 0
9.142 - self.result_target_name = None
9.143
9.144 # Volatile locals for exception handling.
9.145
9.146 @@ -885,6 +901,11 @@
9.147
9.148 if self.in_method():
9.149 for name in self.importer.function_attr_initialisers.get(function_name) or []:
9.150 +
9.151 + # Treat each assignment as a separate statement.
9.152 +
9.153 + self.reset_temp_counters()
9.154 +
9.155 self.process_assignment_node(
9.156 compiler.ast.AssAttr(compiler.ast.Name("self"), name, "OP_ASSIGN"),
9.157 compiler.ast.Name(name))
9.158 @@ -1297,7 +1318,7 @@
9.159 self.record_temp("__tmp_results")
9.160 self.next_result()
9.161 else:
9.162 - result_target = "__NULL"
9.163 + result_target = None
9.164
9.165 # Arguments are presented in a temporary frame array with any context
9.166 # always being the first argument. Where it would be unused, it may be
9.167 @@ -1313,7 +1334,7 @@
9.168
9.169 # Start with result target and context arguments for each invocation.
9.170
9.171 - args = [result_target, context_arg]
9.172 + args = [result_target or "__NULL", context_arg]
9.173 reserved_args = 2
9.174
9.175 # Complete the array with null values, permitting tests for a complete
9.176 @@ -1347,8 +1368,8 @@
9.177
9.178 # Convert any attributes indicating value replacement.
9.179
9.180 - if isinstance(argexpr, InvocationResult):
9.181 - argexprstr = "__set_local(&%s, %s)" % (argexpr.result_target, argexpr)
9.182 + if isinstance(argexpr, InvocationResult) and argexpr.result_target:
9.183 + argexprstr = "__set_attr(&%s, %s)" % (argexpr.result_target, argexpr)
9.184 else:
9.185 argexprstr = str(argexpr)
9.186
9.187 @@ -1528,6 +1549,8 @@
9.188 self.function_target = 0
9.189 self.context_index = 0
9.190 self.accessor_index = 0
9.191 + self.attribute_ref_index = 0
9.192 + self.result_target_name = None
9.193
9.194 def reset_temp_limits(self):
9.195
9.196 @@ -1536,6 +1559,7 @@
9.197 self.max_function_target = 0
9.198 self.max_context_index = 0
9.199 self.max_accessor_index = 0
9.200 + self.max_attribute_ref_index = 0
9.201
9.202 def next_result(self):
9.203
9.204 @@ -1565,6 +1589,13 @@
9.205 self.accessor_index += 1
9.206 self.max_accessor_index = max(self.accessor_index, self.max_accessor_index)
9.207
9.208 + def next_attribute_ref(self):
9.209 +
9.210 + "Allocate the next attribute reference value storage."
9.211 +
9.212 + self.attribute_ref_index += 1
9.213 + self.max_attribute_ref_index = max(self.attribute_ref_index, self.max_attribute_ref_index)
9.214 +
9.215 def always_callable(self, refs):
9.216
9.217 "Determine whether all 'refs' are callable."
9.218 @@ -1724,12 +1755,13 @@
9.219 if expr and self.in_function and not is_global and self.in_try_except:
9.220 self.make_volatile(n.name)
9.221
9.222 - # Set the replacement target for local objects.
9.223 - # Note that this will not apply to all local objects, with only floats
9.224 - # supporting replaceable values.
9.225 -
9.226 - if expr and self.in_function and not is_global:
9.227 - self.result_target_name = encode_path(n.name)
9.228 + # Set the replacement target. Note that this will not apply to all
9.229 + # objects, with only floats supporting replaceable values.
9.230 +
9.231 + if expr:
9.232 + target_ref = TrResolvedNameRef(n.name, ref, is_global=is_global,
9.233 + location=location)
9.234 + self.result_target_name = str(target_ref)
9.235
9.236 # Expression processing is deferred until after any result target has
9.237 # been set.
9.238 @@ -1743,6 +1775,7 @@
9.239
9.240 name_ref = TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global,
9.241 location=location)
9.242 +
9.243 return not expr and self.get_aliases(name_ref) or name_ref
9.244
9.245 def get_aliases(self, name_ref):
9.246 @@ -2246,7 +2279,8 @@
9.247 if self.uses_temp(name, "__tmp_values"):
9.248 self.writeline("__attr __tmp_values[%d];" % self.max_accessor_index)
9.249
9.250 - # Add temporary variable usage details.
9.251 + if self.uses_temp(name, "__tmp_attr_refs"):
9.252 + self.writeline("__attr *__tmp_attr_refs[%d];" % self.max_attribute_ref_index)
9.253
9.254 if self.uses_temp(name, "__tmp_results"):
9.255 self.writeline("__attr __tmp_results[%d] = {0};" % self.max_result_target)
10.1 --- a/transresults.py Sat Sep 02 01:19:52 2023 +0200
10.2 +++ b/transresults.py Sat Sep 02 01:48:44 2023 +0200
10.3 @@ -109,14 +109,14 @@
10.4 # Qualified names must be converted into parent-relative assignments.
10.5
10.6 elif self.parent:
10.7 - return "__store_via_object(&%s, %s, %s)" % (
10.8 + return "__store_via_attr_ref(__get_object_attr_ref(&%s, %s), %s)" % (
10.9 encode_path(self.parent), self.attrname, self.expr)
10.10
10.11 # All other assignments involve the names as they were given.
10.12 # To support value replacement, a special operation is used.
10.13
10.14 else:
10.15 - return "__set_local(&%s, %s)" % (self.attrname, self.expr)
10.16 + return "__set_attr(&%s, %s)" % (self.attrname, self.expr)
10.17
10.18 # Expressions.
10.19
10.20 @@ -179,7 +179,8 @@
10.21 "A translation result for an attribute access."
10.22
10.23 def __init__(self, instructions, refs, location, context_identity,
10.24 - context_identity_verified, accessor_test, accessor_stored):
10.25 + context_identity_verified, accessor_test, accessor_stored,
10.26 + attribute_ref_stored):
10.27
10.28 InstructionSequence.__init__(self, instructions)
10.29 self.refs = refs
10.30 @@ -188,6 +189,7 @@
10.31 self.context_identity_verified = context_identity_verified
10.32 self.accessor_test = accessor_test
10.33 self.accessor_stored = accessor_stored
10.34 + self.attribute_ref_stored = attribute_ref_stored
10.35
10.36 def references(self):
10.37 return self.refs
10.38 @@ -207,6 +209,9 @@
10.39 def stores_accessor(self):
10.40 return self.accessor_stored
10.41
10.42 + def stores_attribute_ref(self):
10.43 + return self.attribute_ref_stored
10.44 +
10.45 def get_origin(self):
10.46 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
10.47
10.48 @@ -225,10 +230,11 @@
10.49 return encode_instructions(self.instructions)
10.50
10.51 def __repr__(self):
10.52 - return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % (
10.53 + return "AttrResult(%r, %r, %r, %r, %r, %r, %r, %r)" % (
10.54 self.instructions, self.refs, self.location,
10.55 self.context_identity, self.context_identity_verified,
10.56 - self.accessor_test, self.accessor_stored)
10.57 + self.accessor_test, self.accessor_stored,
10.58 + self.attribute_ref_stored)
10.59
10.60 class AliasResult(NameRef, Result):
10.61