1.1 --- a/translator.py Sat Sep 02 01:19:52 2023 +0200
1.2 +++ b/translator.py Sat Sep 02 01:48:44 2023 +0200
1.3 @@ -332,8 +332,6 @@
1.4 # Handle processing requests on nodes.
1.5
1.6 else:
1.7 - self.reset_temp_counters()
1.8 -
1.9 l = CommonModule.process_structure(self, node)
1.10
1.11 # Return indications of return statement usage.
1.12 @@ -343,6 +341,13 @@
1.13 else:
1.14 return None
1.15
1.16 + def process_statement_node(self, node):
1.17 +
1.18 + "Process the given statement 'node'."
1.19 +
1.20 + self.reset_temp_counters()
1.21 + return CommonModule.process_statement_node(self, node)
1.22 +
1.23 def process_structure_node(self, n):
1.24
1.25 "Process the individual node 'n'."
1.26 @@ -501,6 +506,7 @@
1.27
1.28 elif isinstance(n, compiler.ast.AssAttr):
1.29 in_assignment = self.in_assignment
1.30 + self.result_target_name = "*__get_attr_ref(%d)" % self.attribute_ref_index
1.31 self.in_assignment = self.process_structure_node(expr)
1.32 self.statement(self.process_attribute_access(n))
1.33 self.in_assignment = in_assignment
1.34 @@ -556,12 +562,14 @@
1.35 # invocation.
1.36
1.37 accessor_index = self.accessor_index
1.38 + attribute_ref_index = self.attribute_ref_index
1.39 context_index = self.context_index
1.40 context_identity = None
1.41 context_identity_verified = False
1.42 final_identity = None
1.43 accessor_test = False
1.44 accessor_stored = False
1.45 + attribute_ref_stored = False
1.46
1.47 # Obtain encoded versions of each instruction, accumulating temporary
1.48 # variables.
1.49 @@ -573,7 +581,8 @@
1.50 if instruction[0] in ("<context_identity>", "<context_identity_verified>"):
1.51 context_identity, _substituted = \
1.52 encode_access_instruction_arg(instruction[1], subs, instruction[0],
1.53 - accessor_index, context_index)
1.54 + accessor_index, context_index,
1.55 + attribute_ref_index)
1.56 context_identity_verified = instruction[0] == "<context_identity_verified>"
1.57 continue
1.58
1.59 @@ -595,11 +604,18 @@
1.60 elif instruction[0] == "<set_accessor>":
1.61 accessor_stored = True
1.62
1.63 + # Intercept attribute reference storage.
1.64 +
1.65 + elif instruction[0] == "<set_attr_ref>":
1.66 + attribute_ref_stored = True
1.67 + self.next_attribute_ref()
1.68 +
1.69 # Collect the encoded instruction, noting any temporary variables
1.70 # required by it.
1.71
1.72 encoded, _substituted = encode_access_instruction(instruction, subs,
1.73 - accessor_index, context_index)
1.74 + accessor_index, context_index,
1.75 + attribute_ref_index)
1.76 output.append(encoded)
1.77 substituted.update(_substituted)
1.78
1.79 @@ -617,7 +633,7 @@
1.80 del self.attrs[0]
1.81 return AttrResult(output, refs, location,
1.82 context_identity, context_identity_verified,
1.83 - accessor_test, accessor_stored)
1.84 + accessor_test, accessor_stored, attribute_ref_stored)
1.85
1.86 def init_substitutions(self):
1.87
1.88 @@ -637,23 +653,27 @@
1.89 # Mappings to be replaced by those given below.
1.90
1.91 "<accessor>" : "__tmp_values",
1.92 + "<attr_ref>" : "__tmp_attr_refs",
1.93 "<context>" : "__tmp_contexts",
1.94 "<test_context_revert>" : "__tmp_contexts",
1.95 "<test_context_static>" : "__tmp_contexts",
1.96 "<set_context>" : "__tmp_contexts",
1.97 "<set_private_context>" : "__tmp_private_context",
1.98 "<set_accessor>" : "__tmp_values",
1.99 + "<set_attr_ref>" : "__tmp_attr_refs",
1.100 "<set_target_accessor>" : "__tmp_target_value",
1.101 }
1.102
1.103 self.op_subs = {
1.104 "<accessor>" : "__get_accessor",
1.105 + "<attr_ref>" : "__get_attr_ref",
1.106 "<context>" : "__get_context",
1.107 "<test_context_revert>" : "__test_context_revert",
1.108 "<test_context_static>" : "__test_context_static",
1.109 "<set_context>" : "__set_context",
1.110 "<set_private_context>" : "__set_private_context",
1.111 "<set_accessor>" : "__set_accessor",
1.112 + "<set_attr_ref>" : "__set_attr_ref",
1.113 "<set_target_accessor>" : "__set_target_accessor",
1.114 }
1.115
1.116 @@ -804,7 +824,7 @@
1.117 if ref and not ref.static():
1.118 parent, attrname = path.rsplit(".", 1)
1.119
1.120 - self.writestmt("__store_via_object(&%s, %s, __load_via_object(&%s, %s));" % (
1.121 + self.writestmt("__store_via_attr_ref(__get_object_attr_ref(&%s, %s), __load_via_object(&%s, %s));" % (
1.122 encode_path(class_name), name,
1.123 encode_path(parent), attrname
1.124 ))
1.125 @@ -851,19 +871,15 @@
1.126 in_conditional = self.in_conditional
1.127 self.in_conditional = False
1.128
1.129 - # Reset temporary storage counters and limits. The counters are reset
1.130 - # for every statement, but are also involved in expressions processed
1.131 - # for this node.
1.132 + # Reset temporary storage counters and limits.
1.133
1.134 self.reset_temp_limits()
1.135 - self.reset_temp_counters()
1.136
1.137 # Reset result target storage details. To be effective, storage
1.138 # locations are not reused between statements.
1.139
1.140 self.max_result_target = 0
1.141 self.result_target = 0
1.142 - self.result_target_name = None
1.143
1.144 # Volatile locals for exception handling.
1.145
1.146 @@ -885,6 +901,11 @@
1.147
1.148 if self.in_method():
1.149 for name in self.importer.function_attr_initialisers.get(function_name) or []:
1.150 +
1.151 + # Treat each assignment as a separate statement.
1.152 +
1.153 + self.reset_temp_counters()
1.154 +
1.155 self.process_assignment_node(
1.156 compiler.ast.AssAttr(compiler.ast.Name("self"), name, "OP_ASSIGN"),
1.157 compiler.ast.Name(name))
1.158 @@ -1297,7 +1318,7 @@
1.159 self.record_temp("__tmp_results")
1.160 self.next_result()
1.161 else:
1.162 - result_target = "__NULL"
1.163 + result_target = None
1.164
1.165 # Arguments are presented in a temporary frame array with any context
1.166 # always being the first argument. Where it would be unused, it may be
1.167 @@ -1313,7 +1334,7 @@
1.168
1.169 # Start with result target and context arguments for each invocation.
1.170
1.171 - args = [result_target, context_arg]
1.172 + args = [result_target or "__NULL", context_arg]
1.173 reserved_args = 2
1.174
1.175 # Complete the array with null values, permitting tests for a complete
1.176 @@ -1347,8 +1368,8 @@
1.177
1.178 # Convert any attributes indicating value replacement.
1.179
1.180 - if isinstance(argexpr, InvocationResult):
1.181 - argexprstr = "__set_local(&%s, %s)" % (argexpr.result_target, argexpr)
1.182 + if isinstance(argexpr, InvocationResult) and argexpr.result_target:
1.183 + argexprstr = "__set_attr(&%s, %s)" % (argexpr.result_target, argexpr)
1.184 else:
1.185 argexprstr = str(argexpr)
1.186
1.187 @@ -1528,6 +1549,8 @@
1.188 self.function_target = 0
1.189 self.context_index = 0
1.190 self.accessor_index = 0
1.191 + self.attribute_ref_index = 0
1.192 + self.result_target_name = None
1.193
1.194 def reset_temp_limits(self):
1.195
1.196 @@ -1536,6 +1559,7 @@
1.197 self.max_function_target = 0
1.198 self.max_context_index = 0
1.199 self.max_accessor_index = 0
1.200 + self.max_attribute_ref_index = 0
1.201
1.202 def next_result(self):
1.203
1.204 @@ -1565,6 +1589,13 @@
1.205 self.accessor_index += 1
1.206 self.max_accessor_index = max(self.accessor_index, self.max_accessor_index)
1.207
1.208 + def next_attribute_ref(self):
1.209 +
1.210 + "Allocate the next attribute reference value storage."
1.211 +
1.212 + self.attribute_ref_index += 1
1.213 + self.max_attribute_ref_index = max(self.attribute_ref_index, self.max_attribute_ref_index)
1.214 +
1.215 def always_callable(self, refs):
1.216
1.217 "Determine whether all 'refs' are callable."
1.218 @@ -1724,12 +1755,13 @@
1.219 if expr and self.in_function and not is_global and self.in_try_except:
1.220 self.make_volatile(n.name)
1.221
1.222 - # Set the replacement target for local objects.
1.223 - # Note that this will not apply to all local objects, with only floats
1.224 - # supporting replaceable values.
1.225 -
1.226 - if expr and self.in_function and not is_global:
1.227 - self.result_target_name = encode_path(n.name)
1.228 + # Set the replacement target. Note that this will not apply to all
1.229 + # objects, with only floats supporting replaceable values.
1.230 +
1.231 + if expr:
1.232 + target_ref = TrResolvedNameRef(n.name, ref, is_global=is_global,
1.233 + location=location)
1.234 + self.result_target_name = str(target_ref)
1.235
1.236 # Expression processing is deferred until after any result target has
1.237 # been set.
1.238 @@ -1743,6 +1775,7 @@
1.239
1.240 name_ref = TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global,
1.241 location=location)
1.242 +
1.243 return not expr and self.get_aliases(name_ref) or name_ref
1.244
1.245 def get_aliases(self, name_ref):
1.246 @@ -2246,7 +2279,8 @@
1.247 if self.uses_temp(name, "__tmp_values"):
1.248 self.writeline("__attr __tmp_values[%d];" % self.max_accessor_index)
1.249
1.250 - # Add temporary variable usage details.
1.251 + if self.uses_temp(name, "__tmp_attr_refs"):
1.252 + self.writeline("__attr *__tmp_attr_refs[%d];" % self.max_attribute_ref_index)
1.253
1.254 if self.uses_temp(name, "__tmp_results"):
1.255 self.writeline("__attr __tmp_results[%d] = {0};" % self.max_result_target)