# HG changeset patch # User Paul Boddie # Date 1693941409 -7200 # Node ID 582d834d392df5dfa7e48401e82d14d6549a9a01 # Parent f646afbcfe1c7a74a8f75210c2984346dae223d1# Parent 06335a0e411366319ffbf4115086b8161d0cc28b Merged changes from the value-replacement branch. diff -r f646afbcfe1c -r 582d834d392d access_plan.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/access_plan.py Tue Sep 05 21:16:49 2023 +0200 @@ -0,0 +1,545 @@ +#!/usr/bin/env python + +""" +Attribute access plan translation. + +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 +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from encoders import encode_access_location + +class AccessPlan: + + "An attribute access plan." + + def __init__(self, name, test, test_type, base, traversed, traversal_modes, + remaining, context, context_test, first_method, final_method, + origin, accessor_kinds): + + "Initialise the plan." + + # With instance attribute initialisers, the assignment below would be + # generated automatically. + + ( + self.name, self.test, self.test_type, self.base, + self.traversed, self.traversal_modes, self.remaining, + self.context, self.context_test, + self.first_method, self.final_method, + self.origin, self.accessor_kinds) = ( + + name, test, test_type, base, + traversed, traversal_modes, remaining, + context, context_test, + first_method, final_method, + origin, accessor_kinds) + + # Define the first attribute access and subsequent accesses. + + self.first_attrname = None + self.traversed_attrnames = traversed + self.traversed_attrname_modes = traversal_modes + self.remaining_attrnames = remaining + + if traversed: + self.first_attrname = traversed[0] + self.traversed_attrnames = traversed[1:] + self.traversed_attrname_modes = traversal_modes[1:] + elif remaining: + self.first_attrname = remaining[0] + self.remaining_attrnames = remaining[1:] + + def access_first_attribute(self): + + "Return whether the first attribute is to be accessed." + + return self.final_method in ("access", "access-invoke", "assign") or \ + self.all_subsequent_attributes() + + def assigning_first_attribute(self): + + "Return whether the first attribute access involves assignment." + + return not self.all_subsequent_attributes() and self.final_method == "assign" + + def get_first_attribute_name(self): + + "Return any first attribute name to be used in an initial access." + + return self.first_attrname + + def all_subsequent_attributes(self): + + "Return all subsequent attribute names involved in accesses." + + return self.traversed_attrnames + self.remaining_attrnames + + def attribute_traversals(self): + + "Return a collection of (attribute name, traversal mode) tuples." + + return zip(self.traversed_attrnames, self.traversed_attrname_modes) + + def stored_accessor(self): + + "Return the variable used to obtain the accessor." + + return self.assigning_first_attribute() and "" or "" + + def stored_accessor_modifier(self): + + "Return the variable used to set the accessor." + + return self.assigning_first_attribute() and "" or "" + + def get_original_accessor(self): + + "Return the original accessor details." + + # Identify any static original accessor. + + if self.base: + return self.base + + # Employ names as contexts unless the context needs testing and + # potentially updating. In such cases, temporary context storage is + # used instead. + + elif self.name and not (self.context_test == "test" and + self.final_method in ("access-invoke", "static-invoke")): + + return "" + + # Use a generic placeholder representing the access expression in + # the general case. + + else: + return "" + + def get_instructions(self): + + "Return a list of instructions corresponding to the plan." + + # Emit instructions by appending them to a list. + + instructions = [] + emit = instructions.append + + # Set up any initial instructions. + + accessor, context = self.process_initialisation(emit) + + # Apply any test. + + if self.test[0] == "test": + test_accessor = accessor = ("__%s_%s_%s" % self.test, accessor, self.test_type) + else: + test_accessor = None + + # Perform the first or final access. + # The access only needs performing if the resulting accessor is used. + + accessor = self.process_first_attribute(accessor, emit) + + # Perform accesses for the traversed and remaining attributes. + + accessor, context = self.process_traversed_attributes(accessor, context, emit) + accessor, context = self.process_remaining_attributes(accessor, context, emit) + + # Make any accessor test available if not emitted. + + test_accessor = not instructions and test_accessor or None + + # Perform the access on the actual target. + + accessor, context = self.process_attribute_access(accessor, context, test_accessor, emit) + + # Produce an advisory instruction regarding the context. + + self.process_context_identity(context, emit) + + # Produce an advisory instruction regarding the final attribute. + + if self.origin: + emit(("", self.origin)) + + return instructions + + def process_initialisation(self, emit): + + """ + Use 'emit' to generate instructions for any initialisation of attribute + access. Return the potentially revised accessor and context indicators. + """ + + # Identify any static original accessor. + + original_accessor = self.get_original_accessor() + + # Determine whether the first access involves assignment. + + set_accessor = self.stored_accessor_modifier() + stored_accessor = self.stored_accessor() + + # Set the context if already available. + + context = None + + if self.context == "base": + accessor = context = (self.base,) + elif self.context == "original-accessor": + + # Prevent re-evaluation of any dynamic expression by storing it. + + if original_accessor == "": + if self.final_method in ("access-invoke", "static-invoke"): + emit(("", original_accessor)) + accessor = context = ("",) + else: + emit((set_accessor, original_accessor)) + accessor = context = (stored_accessor,) + else: + accessor = context = (original_accessor,) + + # Assigning does not set the context. + + elif self.context in ("final-accessor", "unset") and self.access_first_attribute(): + + # Prevent re-evaluation of any dynamic expression by storing it. + + if original_accessor == "": + emit((set_accessor, original_accessor)) + accessor = (stored_accessor,) + else: + accessor = (original_accessor,) + else: + accessor = None + + return accessor, context + + def process_first_attribute(self, accessor, emit): + + """ + Using 'accessor', use 'emit' to generate instructions for any first + attribute access. Return the potentially revised accessor. + """ + + if self.access_first_attribute(): + attrname = self.get_first_attribute_name() + assigning = self.assigning_first_attribute() + + store = attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" + + # Access via the accessor's class. + + if self.first_method == "relative-class": + if assigning: + emit(("", ("__get_class_attr_ref", accessor, attrname))) + emit((store, "", "")) + else: + accessor = ("__load_via_class", accessor, attrname) + + # Access via the accessor itself. + + elif self.first_method == "relative-object": + if assigning: + emit(("", ("__get_object_attr_ref", accessor, attrname))) + emit((store, "", "")) + else: + accessor = ("__load_via_object", accessor, attrname) + + # Access via a class accessor or the accessor's class. + + elif self.first_method == "relative-object-class": + if assigning: + emit(("__raise_type_error",)) + else: + accessor = ("__get_class_and_load", accessor, attrname) + + # Access via the accessor's class. + + elif self.first_method == "check-class": + if assigning: + emit(("__raise_type_error",)) + else: + accessor = ("__check_and_load_via_class", accessor, attrname) + + # Access via the accessor itself. + + elif self.first_method == "check-object": + if assigning: + emit(("", ("__check_and_get_object_attr_ref", accessor, attrname))) + emit((store, "", "")) + 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 self.first_method == "check-object-class": + if assigning: + emit(("", ("__check_and_get_object_attr_ref", accessor, attrname))) + emit((store, "", "")) + else: + accessor = ("__check_and_load_via_any", accessor, attrname) + + return accessor + + def process_traversed_attributes(self, accessor, context, emit): + + """ + Using 'accessor' and 'context', use 'emit' to generate instructions + for the traversed attribute accesses. Return the potentially revised + accessor and context indicators. + """ + + # Traverse attributes using the accessor. + + num_remaining = len(self.all_subsequent_attributes()) + + if self.traversed_attrnames: + for attrname, traversal_mode in self.attribute_traversals(): + assigning = num_remaining == 1 and self.final_method == "assign" + + # Set the context, if appropriate. + + if num_remaining == 1 and self.final_method != "assign" and self.context == "final-accessor": + + # Invoked attributes employ a separate context accessed + # during invocation. + + if self.final_method in ("access-invoke", "static-invoke"): + emit(("", accessor)) + accessor = context = "" + + # A private context within the access is otherwise + # retained. + + else: + emit(("", accessor)) + accessor = context = "" + + # Perform the access only if not achieved directly. + + if num_remaining > 1 or self.final_method in ("access", "access-invoke", "assign"): + + store = attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" + + if traversal_mode == "class": + if assigning: + emit(("", ("__get_class_attr_ref", accessor, attrname))) + emit((store, "", "")) + else: + accessor = ("__load_via_class", accessor, attrname) + else: + if assigning: + emit(("", ("__get_object_attr_ref", accessor, attrname))) + emit((store, "", "")) + else: + accessor = ("__load_via_object", accessor, attrname) + + num_remaining -= 1 + + return accessor, context + + def process_remaining_attributes(self, accessor, context, emit): + + """ + Using 'accessor' and 'context', use 'emit' to generate instructions + for the remaining attribute accesses. Return the potentially revised + accessor and context indicators. + """ + + remaining = self.remaining_attrnames + + if remaining: + num_remaining = len(remaining) + + for attrname in remaining: + assigning = num_remaining == 1 and self.final_method == "assign" + + # Set the context, if appropriate. + + if num_remaining == 1 and self.final_method != "assign" and self.context == "final-accessor": + + # Invoked attributes employ a separate context accessed + # during invocation. + + if self.final_method in ("access-invoke", "static-invoke"): + emit(("", accessor)) + accessor = context = "" + + # A private context within the access is otherwise + # retained. + + else: + emit(("", accessor)) + accessor = context = "" + + # Perform the access only if not achieved directly. + + if num_remaining > 1 or self.final_method in ("access", "access-invoke", "assign"): + + # Constrain instructions involving certain special + # attribute names. + + store = attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" + to_search = attrname != "__data__" and "any" or "object" + + if assigning: + emit(("", ("__check_and_get_object_attr_ref", accessor, attrname))) + emit((store, "", "")) + else: + accessor = ("__check_and_load_via_%s" % to_search, accessor, attrname) + + num_remaining -= 1 + + return accessor, context + + def process_attribute_access(self, accessor, context, test_accessor, emit): + + """ + Using 'accessor','context' and any 'test_accessor' operation, use 'emit' + to generate instructions for the final attribute access. Return the + potentially revised accessor and context indicators. + """ + + # Define any replacement context variable plus the default eventual + # context variable. + + replacement_context = "" + final_context = context + + # Define or emit the means of accessing the actual target. + + if self.final_method in ("static", "static-assign", "static-invoke"): + + if test_accessor: + emit(test_accessor) + + # Assignments to known attributes. + + if self.final_method == "static-assign": + parent, attrname = self.origin.rsplit(".", 1) + store = attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" + emit(("", ("__get_object_attr_ref", parent, attrname))) + emit((store, "", "")) + + # Invoked attributes employ a separate context. + + elif self.final_method in ("static", "static-invoke"): + accessor = ("__load_static_ignore", self.origin) + + # Wrap accesses in context operations. + + if self.context_test == "test": + + # Test and combine the context with static attribute details. + + if self.final_method == "static": + emit(("__load_static_test", replacement_context, context, self.origin)) + final_context = replacement_context + + # Test the context, storing it separately if required for the + # immediately invoked static attribute. + + elif self.final_method == "static-invoke": + emit(("", context, self.origin)) + + # Test the context, storing it separately if required for an + # immediately invoked attribute. + + elif self.final_method == "access-invoke": + emit(("", context, accessor)) + + # Test the context and update the attribute details if + # appropriate. + + else: + emit(("__test_context", replacement_context, context, accessor)) + final_context = replacement_context + + elif self.context_test == "replace": + + # Produce an object with updated context. + + if self.final_method == "static": + emit(("__load_static_replace", replacement_context, context, self.origin)) + final_context = replacement_context + + # Omit the context update operation where the target is static + # and the context is recorded separately. + + elif self.final_method == "static-invoke": + pass + + # If a separate context is used for an immediate invocation, + # produce the attribute details unchanged. + + elif self.final_method == "access-invoke": + emit(accessor) + + # Update the context in the attribute details. + + else: + emit(("__update_context", replacement_context, context, accessor)) + final_context = replacement_context + + # Omit the accessor for assignments and for invocations of static + # targets. Otherwise, emit the accessor which may involve the + # invocation of a test. + + elif self.final_method not in ("assign", "static-assign", "static-invoke"): + emit(accessor) + + return accessor, final_context + + def process_context_identity(self, context, emit): + + """ + Using 'context', use 'emit' to generate instructions to test the context + identity. + """ + + if context: + + # Only verify the context for invocation purposes if a suitable + # test has been performed. + + if self.context_test in ("ignore", "replace") or \ + self.final_method in ("access-invoke", "static-invoke"): + + emit(("", context)) + else: + emit(("", context)) + + def write(self, f, location): + + "Write the plan to file 'f' with the given 'location' information." + + print >>f, encode_access_location(location), \ + self.name or "{}", \ + self.test and "-".join(self.test) or "{}", \ + self.test_type or "{}", \ + self.base or "{}", \ + ".".join(self.traversed_attrnames) or "{}", \ + ".".join(self.traversed_attrname_modes) or "{}", \ + ".".join(self.remaining_attrnames) or "{}", \ + self.context, self.context_test, \ + self.first_method, self.final_method, self.origin or "{}", \ + ",".join(self.accessor_kinds) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r f646afbcfe1c -r 582d834d392d deducer.py --- a/deducer.py Sun Sep 03 00:24:29 2023 +0200 +++ b/deducer.py Tue Sep 05 21:16:49 2023 +0200 @@ -19,6 +19,7 @@ this program. If not, see . """ +from access_plan import AccessPlan from common import first, get_assigned_attributes, get_attrnames, \ get_invoked_attributes, get_name_path, init_item, \ order_dependencies_partial, sorted_output, \ @@ -477,23 +478,7 @@ locations.sort() for location in locations: - name, test, test_type, base, \ - traversed, traversal_modes, attrnames, \ - context, context_test, \ - first_method, final_method, \ - attr, accessor_kinds = self.access_plans[location] - - print >>f_attrs, encode_access_location(location), \ - name or "{}", \ - test and "-".join(test) or "{}", \ - test_type or "{}", \ - base or "{}", \ - ".".join(traversed) or "{}", \ - ".".join(traversal_modes) or "{}", \ - ".".join(attrnames) or "{}", \ - context, context_test, \ - first_method, final_method, attr or "{}", \ - ",".join(accessor_kinds) + self.access_plans[location].write(f_attrs, location) finally: f_attrs.close() @@ -2637,364 +2622,18 @@ (base and "base" or "original-accessor") or \ "final-accessor" - return name, test, test_type, base, \ - traversed, traversal_modes, remaining, \ - context, context_test, \ - first_method, final_method, \ - origin, accessor_kinds + return AccessPlan(name, test, test_type, base, + traversed, traversal_modes, remaining, + context, context_test, + first_method, final_method, + origin, accessor_kinds) def initialise_access_instructions(self): "Expand access plans into instruction sequences." for access_location, access_plan in self.access_plans.items(): - - # Obtain the access details. - - name, test, test_type, base, \ - traversed, traversal_modes, attrnames, \ - context, context_test, \ - first_method, final_method, \ - origin, accessor_kinds = access_plan - - # Emit instructions by appending them to a list. - - instructions = [] - emit = instructions.append - - # Identify any static original accessor. - - if base: - original_accessor = base - - # Employ names as contexts unless the context needs testing and - # potentially updating. In such cases, temporary context storage is - # used instead. - - elif name and not (context_test == "test" and - final_method in ("access-invoke", "static-invoke")): - original_accessor = "" # refers to the name - - # Use a generic placeholder representing the access expression in - # the general case. - - else: - original_accessor = "" - - # Prepare for any first attribute access. - - if traversed: - attrname = traversed[0] - del traversed[0] - elif attrnames: - attrname = attrnames[0] - del attrnames[0] - - # Perform the first access explicitly if at least one operation - # requires it. - - access_first_attribute = final_method in ("access", "access-invoke", "assign") or traversed or attrnames - - # Determine whether the first access involves assignment. - - assigning = not traversed and not attrnames and final_method == "assign" - set_accessor = assigning and "" or "" - stored_accessor = assigning and "" or "" - - # Set the context if already available. - - context_var = None - - if context == "base": - accessor = context_var = (base,) - elif context == "original-accessor": - - # Prevent re-evaluation of any dynamic expression by storing it. - - if original_accessor == "": - if final_method in ("access-invoke", "static-invoke"): - emit(("", original_accessor)) - accessor = context_var = ("",) - else: - emit((set_accessor, original_accessor)) - accessor = context_var = (stored_accessor,) - else: - accessor = context_var = (original_accessor,) - - # Assigning does not set the context. - - elif context in ("final-accessor", "unset") and access_first_attribute: - - # Prevent re-evaluation of any dynamic expression by storing it. - - if original_accessor == "": - emit((set_accessor, original_accessor)) - accessor = (stored_accessor,) - else: - accessor = (original_accessor,) - else: - accessor = None - - # Apply any test. - - if test[0] == "test": - test_accessor = accessor = ("__%s_%s_%s" % test, accessor, test_type) - else: - test_accessor = None - - # Perform the first or final access. - # The access only needs performing if the resulting accessor is used. - - remaining = len(traversed + attrnames) - - if access_first_attribute: - - # Access via the accessor's class. - - if first_method == "relative-class": - if assigning: - 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(("", ("__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(("__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(("__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_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_get_object_attr_ref", accessor, attrname))) - emit(("__store_via_attr_ref", "", "")) - else: - accessor = ("__check_and_load_via_any", accessor, attrname) - - # Traverse attributes using the accessor. - - if traversed: - for attrname, traversal_mode in zip(traversed, traversal_modes): - assigning = remaining == 1 and final_method == "assign" - - # Set the context, if appropriate. - - if remaining == 1 and final_method != "assign" and context == "final-accessor": - - # Invoked attributes employ a separate context accessed - # during invocation. - - if final_method in ("access-invoke", "static-invoke"): - emit(("", accessor)) - accessor = context_var = "" - - # A private context within the access is otherwise - # retained. - - else: - emit(("", accessor)) - accessor = context_var = "" - - # Perform the access only if not achieved directly. - - if remaining > 1 or final_method in ("access", "access-invoke", "assign"): - - if traversal_mode == "class": - if assigning: - emit(("", ("__get_class_attr_ref", accessor, attrname))) - emit(("__store_via_attr_ref", "", "")) - else: - accessor = ("__load_via_class", accessor, attrname) - else: - if assigning: - emit(("", ("__get_object_attr_ref", accessor, attrname))) - emit(("__store_via_attr_ref", "", "")) - else: - accessor = ("__load_via_object", accessor, attrname) - - remaining -= 1 - - if attrnames: - for attrname in attrnames: - assigning = remaining == 1 and final_method == "assign" - - # Set the context, if appropriate. - - if remaining == 1 and final_method != "assign" and context == "final-accessor": - - # Invoked attributes employ a separate context accessed - # during invocation. - - if final_method in ("access-invoke", "static-invoke"): - emit(("", accessor)) - accessor = context_var = "" - - # A private context within the access is otherwise - # retained. - - else: - emit(("", accessor)) - accessor = context_var = "" - - # Perform the access only if not achieved directly. - - if remaining > 1 or final_method in ("access", "access-invoke", "assign"): - - # Constrain instructions involving certain special - # attribute names. - - to_search = attrname != "__data__" and "any" or "object" - - if assigning: - 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) - - remaining -= 1 - - # Make any accessor test available if not emitted. - - test_accessor = not instructions and test_accessor or None - - # Define or emit the means of accessing the actual target. - - if final_method in ("static", "static-assign", "static-invoke"): - - if test_accessor: - emit(test_accessor) - - # Assignments to known attributes. - - if final_method == "static-assign": - parent, attrname = origin.rsplit(".", 1) - emit(("", ("__get_object_attr_ref", parent, attrname))) - emit(("__store_via_attr_ref", "", "")) - - # Invoked attributes employ a separate context. - - elif final_method in ("static", "static-invoke"): - accessor = ("__load_static_ignore", origin) - - # Define any replacement context variable plus the default eventual - # context variable. - - replacement_context_var = "" - final_context_var = context_var - - # Wrap accesses in context operations. - - if context_test == "test": - - # Test and combine the context with static attribute details. - - if final_method == "static": - emit(("__load_static_test", replacement_context_var, context_var, origin)) - final_context_var = replacement_context_var - - # Test the context, storing it separately if required for the - # immediately invoked static attribute. - - elif final_method == "static-invoke": - emit(("", context_var, origin)) - - # Test the context, storing it separately if required for an - # immediately invoked attribute. - - elif final_method == "access-invoke": - emit(("", context_var, accessor)) - - # Test the context and update the attribute details if - # appropriate. - - else: - emit(("__test_context", replacement_context_var, context_var, accessor)) - final_context_var = replacement_context_var - - elif context_test == "replace": - - # Produce an object with updated context. - - if final_method == "static": - emit(("__load_static_replace", replacement_context_var, context_var, origin)) - final_context_var = replacement_context_var - - # Omit the context update operation where the target is static - # and the context is recorded separately. - - elif final_method == "static-invoke": - pass - - # If a separate context is used for an immediate invocation, - # produce the attribute details unchanged. - - elif final_method == "access-invoke": - emit(accessor) - - # Update the context in the attribute details. - - else: - emit(("__update_context", replacement_context_var, context_var, accessor)) - final_context_var = replacement_context_var - - # Omit the accessor for assignments and for invocations of static - # targets. Otherwise, emit the accessor which may involve the - # invocation of a test. - - elif final_method not in ("assign", "static-assign", "static-invoke"): - emit(accessor) - - # Produce an advisory instruction regarding the context. - - if final_context_var: - - # 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(("", final_context_var)) - else: - emit(("", final_context_var)) - - # Produce an advisory instruction regarding the final attribute. - - if origin: - emit(("", origin)) - - self.access_instructions[access_location] = instructions - self.accessor_kinds[access_location] = accessor_kinds + self.access_instructions[access_location] = access_plan.get_instructions() + self.accessor_kinds[access_location] = access_plan.accessor_kinds # vim: tabstop=4 expandtab shiftwidth=4 diff -r f646afbcfe1c -r 582d834d392d docs/wiki/Deduction --- a/docs/wiki/Deduction Sun Sep 03 00:24:29 2023 +0200 +++ b/docs/wiki/Deduction Tue Sep 05 21:16:49 2023 +0200 @@ -89,7 +89,7 @@ //format=svg //transform=notugly digraph indexes { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Indexes"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Indexes"]; edge [tooltip="Indexes"]; rankdir=LR; @@ -142,7 +142,7 @@ //format=svg //transform=notugly digraph deduction { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Deduction"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Deduction"]; edge [tooltip="Deduction"]; rankdir=LR; @@ -192,7 +192,7 @@ //format=svg //transform=notugly digraph usage_to_types { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Usage to types"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Usage to types"]; edge [tooltip="Usage to types"]; rankdir=LR; @@ -249,7 +249,7 @@ //format=svg //transform=notugly digraph instance_providers { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Instance providers"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Instance providers"]; edge [tooltip="Instance providers"]; rankdir=LR; @@ -310,7 +310,7 @@ //format=svg //transform=notugly digraph assignments { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Attribute assignments"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Attribute assignments"]; edge [tooltip="Attribute assignments"]; rankdir=LR; @@ -736,19 +736,34 @@ .. position }}} -==== Direct Store ==== +==== Attribute Reference ==== -These instructions employ the attribute position for the supplied attribute -name, storing an attribute value. +Attribute references are obtained to be targets for store operations as well +as being exposed as result targets. {{{#!table '''Instruction''' || '''Arguments''' || '''Operations''' == -`__store_via_class` || object, attribute name, value -|| Obtain class from object; store attribute in class at position +`__get_class_attr_ref` || object, attribute name +|| Obtain class from object; obtain reference for attribute in class == -`__store_via_object` || object, attribute name, value -|| Store attribute in object at position +`__get_object_attr_ref` || object, attribute name +|| Obtain reference for attribute in object +}}} + +==== Attribute Reference Load and Store ==== + +These instructions employ an attribute reference, previously obtained for a +given attribute in an object. + +{{{#!table +'''Instruction''' || '''Arguments''' || '''Operations''' +== +`__load_via_attr_ref` || attribute reference +|| Load value from attribute reference +== +`__store_via_attr_ref` || attribute reference, value +|| Store value in attribute at reference }}} ==== Checked Load ==== @@ -770,7 +785,7 @@ .. raise type error }}} -==== Checked Store ==== +==== Checked Attribute Reference ==== These instructions employ the attribute position and code for the supplied attribute name, storing an attribute value. @@ -778,14 +793,8 @@ {{{#!table '''Instruction''' || '''Arguments''' || '''Operations''' == -`__check_and_store_via_class` || object, attribute name, value -|| Raise type error -== -`__check_and_store_via_object` || object, attribute name, value -|| Test for attribute and store value or raise type error -== -`__check_and_store_via_any` || object, attribute name, value -|| Test for attribute and store value or raise type error +`__check_and_get_object_attr_ref` || object, attribute name +|| Test for attribute and obtain reference or raise type error }}} ==== Testing ==== @@ -844,20 +853,29 @@ {{{#!table '''Instruction''' || '''Arguments''' || '''Operations''' == -`__get_context` || (temporary) -|| Load the context stored in the temporary storage +`__get_accessor` || (temporary) +|| Load the indicated accessor from temporary storage +== +`__get_attr_ref` || (temporary) +|| Load the indicated attribute reference from temporary storage == -`__set_accessor` || accessor -|| Store the accessor temporarily +`__get_context` || (temporary) +|| Load the indicated context from temporary storage +== +`__set_accessor` || (temporary), accessor +|| Store the accessor in temporary storage +== +`__set_attr_ref` || (temporary), reference +|| Store the given attribute reference in temporary storage == `__set_context` || (temporary), context -|| Store the context in the temporary storage +|| Store the given context in temporary storage == `__set_private_context` || context -|| Store the context temporarily +|| Store the context in temporary storage == `__set_target_accessor` || accessor -|| Store the assignment accessor temporarily +|| Store the assignment accessor in temporary storage }}} ==== Context Test ==== diff -r f646afbcfe1c -r 582d834d392d docs/wiki/Imports --- a/docs/wiki/Imports Sun Sep 03 00:24:29 2023 +0200 +++ b/docs/wiki/Imports Tue Sep 05 21:16:49 2023 +0200 @@ -95,7 +95,7 @@ //format=svg //transform=notugly digraph mutual { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Mutually-dependent modules"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Mutually-dependent modules"]; edge [tooltip="Mutually-dependent modules"]; rankdir=LR; @@ -209,7 +209,7 @@ //format=svg //transform=notugly digraph imports { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Import dependencies"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Import dependencies"]; edge [tooltip="Import dependencies"]; rankdir=LR; diff -r f646afbcfe1c -r 582d834d392d docs/wiki/Inspection --- a/docs/wiki/Inspection Sun Sep 03 00:24:29 2023 +0200 +++ b/docs/wiki/Inspection Tue Sep 05 21:16:49 2023 +0200 @@ -179,7 +179,7 @@ //format=svg //transform=notugly digraph accesses { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Names and accesses"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Names and accesses"]; edge [tooltip="Names and accesses"]; rankdir=TB; @@ -316,7 +316,7 @@ //format=svg //transform=notugly digraph usage { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Name and attribute tracking"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Name and attribute tracking"]; edge [tooltip="Name and attribute tracking"]; rankdir=TB; diff -r f646afbcfe1c -r 582d834d392d docs/wiki/Representations --- a/docs/wiki/Representations Sun Sep 03 00:24:29 2023 +0200 +++ b/docs/wiki/Representations Tue Sep 05 21:16:49 2023 +0200 @@ -16,8 +16,8 @@ //format=svg //transform=notugly digraph attributes { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Attributes"]; - edge [fontsize="13.0",fontname="sans-serif",tooltip="Attributes"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Attributes"]; + edge [fontsize="12.0",fontname="sans-serif",tooltip="Attributes"]; rankdir=TB; attrA [label="attribute | { value | reference to object }",shape=record]; @@ -77,8 +77,8 @@ //format=svg //transform=notugly digraph wrappers { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Wrappers"]; - edge [fontsize="13.0",fontname="sans-serif",tooltip="Wrappers"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Wrappers"]; + edge [fontsize="12.0",fontname="sans-serif",tooltip="Wrappers"]; rankdir=TB; inst [label="
instance | { attr1 | reference to method } | { attr2 | value } | ...",shape=record]; @@ -104,8 +104,8 @@ //format=svg //transform=notugly digraph objects { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Objects"]; - edge [fontsize="13.0",fontname="sans-serif",tooltip="Objects"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Objects"]; + edge [fontsize="12.0",fontname="sans-serif",tooltip="Objects"]; rankdir=TB; instC [label="
instance of C | { 0 | reference to\ninstance table } | { __class__ | reference\nto C } | { a | value } | { b | value } | ...",shape=record]; diff -r f646afbcfe1c -r 582d834d392d docs/wiki/Restarted --- a/docs/wiki/Restarted Sun Sep 03 00:24:29 2023 +0200 +++ b/docs/wiki/Restarted Tue Sep 05 21:16:49 2023 +0200 @@ -262,8 +262,8 @@ //format=svg //transform=notugly digraph structures { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Instance and class structures"]; - edge [fontsize="13.0",fontname="sans-serif",tooltip="Instance and class structures"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Instance and class structures"]; + edge [fontsize="12.0",fontname="sans-serif",tooltip="Instance and class structures"]; rankdir=TB; instanceC [label="
instance of C |{ context of a | value of a }|{context of b | value of b }",shape=record]; @@ -285,8 +285,8 @@ //format=svg //transform=notugly digraph methods { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Method structures"]; - edge [fontsize="13.0",fontname="sans-serif",tooltip="Method structures"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Method structures"]; + edge [fontsize="12.0",fontname="sans-serif",tooltip="Method structures"]; rankdir=TB; classC [label="
class C | { context of m | uncallable for m } | ...",shape=record]; diff -r f646afbcfe1c -r 582d834d392d docs/wiki/Structure --- a/docs/wiki/Structure Sun Sep 03 00:24:29 2023 +0200 +++ b/docs/wiki/Structure Tue Sep 05 21:16:49 2023 +0200 @@ -29,7 +29,7 @@ //format=svg //transform=notugly digraph program { - node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Program structure"]; + node [shape=box,fontsize="12.0",fontname="sans-serif",tooltip="Program structure"]; edge [tooltip="Program structure"]; rankdir=LR; diff -r f646afbcfe1c -r 582d834d392d templates/native/common.c --- a/templates/native/common.c Sun Sep 03 00:24:29 2023 +0200 +++ b/templates/native/common.c Tue Sep 05 21:16:49 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_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); + __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_attr_ref(__get_object_attr_ref(__VALUE(attr), __data__), (__attr) {.seqvalue=f}); + __store_via_attr_ref__(__get_object_attr_ref(__VALUE(attr), __data__), (__attr) {.seqvalue=f}); return attr; } diff -r f646afbcfe1c -r 582d834d392d templates/native/list.c --- a/templates/native/list.c Sun Sep 03 00:24:29 2023 +0200 +++ b/templates/native/list.c Tue Sep 05 21:16:49 2023 +0200 @@ -56,7 +56,7 @@ /* Replace the __data__ attribute if appropriate. */ if (newdata != data) - __store_via_attr_ref(__get_object_attr_ref(__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_attr_ref(__get_object_attr_ref(__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 f646afbcfe1c -r 582d834d392d templates/ops.c --- a/templates/ops.c Sun Sep 03 00:24:29 2023 +0200 +++ b/templates/ops.c Tue Sep 05 21:16:49 2023 +0200 @@ -417,7 +417,7 @@ void *__ALLOCATEIM(size_t nmemb, size_t size) { - void *ptr = GC_MALLOC_ATOMIC(nmemb * size); /* sets memory to zero */ + void *ptr = GC_MALLOC_ATOMIC(nmemb * size); if (ptr == NULL) __raise_memory_error(); return ptr; diff -r f646afbcfe1c -r 582d834d392d templates/ops.h --- a/templates/ops.h Sun Sep 03 00:24:29 2023 +0200 +++ b/templates/ops.h Tue Sep 05 21:16:49 2023 +0200 @@ -46,6 +46,8 @@ #define __load_via_attr_ref(ATTR) (*(ATTR)) #define __store_via_attr_ref(ATTR, VALUE) __set_attr(ATTR, VALUE) +#define __store_via_attr_ref__(ATTR, VALUE) (*(ATTR) = VALUE) + /* Direct retrieval operations, returning attributes. */ __attr __load_via_class__(__ref obj, int pos); diff -r f646afbcfe1c -r 582d834d392d templates/progops.c --- a/templates/progops.c Sun Sep 03 00:24:29 2023 +0200 +++ b/templates/progops.c Tue Sep 05 21:16:49 2023 +0200 @@ -32,7 +32,7 @@ { obj->table = table; obj->pos = __INSTANCEPOS; - __store_via_attr_ref(__get_object_attr_ref(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_attr_ref(__get_object_attr_ref(__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_attr_ref(__get_object_attr_ref(__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_attr_ref(__get_object_attr_ref__(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 f646afbcfe1c -r 582d834d392d templates/progops.h --- a/templates/progops.h Sun Sep 03 00:24:29 2023 +0200 +++ b/templates/progops.h Tue Sep 05 21:16:49 2023 +0200 @@ -91,6 +91,6 @@ /* Operations for accessing trailing data. */ #define __get_trailing_data(ATTR, TYPE) (((__OBJTYPE(TYPE) *) ((ATTR).value))->trailing) -#define __set_trailing_data(ATTR, TYPE, VALUE) ((__OBJTYPE(TYPE) *) ((ATTR).value))->trailing = VALUE; +#define __set_trailing_data(ATTR, TYPE, VALUE) ((__OBJTYPE(TYPE) *) ((ATTR).value))->trailing = VALUE #endif /* __PROGOPS_H__ */ diff -r f646afbcfe1c -r 582d834d392d templates/types.h --- a/templates/types.h Sun Sep 03 00:24:29 2023 +0200 +++ b/templates/types.h Tue Sep 05 21:16:49 2023 +0200 @@ -115,8 +115,6 @@ } __obj; -#define __INSTANCE_SIZE(NUMBER) ((NUMBER) * sizeof(__attr) + sizeof(__table *) + sizeof(__ppos)) - /* Fragments are simple collections of attributes employed by sequence types. They provide the basis of lists and tuples. */ diff -r f646afbcfe1c -r 582d834d392d tests/values.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/values.py Tue Sep 05 21:16:49 2023 +0200 @@ -0,0 +1,7 @@ +def test(a): + a = a - 1.0 + return a + +x = 2.0 +print test(x) # 1.0 +print x # 2.0 diff -r f646afbcfe1c -r 582d834d392d translator.py --- a/translator.py Sun Sep 03 00:24:29 2023 +0200 +++ b/translator.py Tue Sep 05 21:16:49 2023 +0200 @@ -1766,9 +1766,17 @@ # 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) + # Prevent parameters from becoming result targets. Otherwise, they + # may inadvertently cause the modification of the supplied object. + + parameters = self.importer.function_parameters.get(path) + + if not parameters or n.name not in parameters: + target_ref = TrResolvedNameRef(n.name, ref, is_global=is_global, + location=location) + self.result_target_name = str(target_ref) + else: + self.result_target_name = None # Expression processing is deferred until after any result target has # been set. diff -r f646afbcfe1c -r 582d834d392d transresults.py --- a/transresults.py Sun Sep 03 00:24:29 2023 +0200 +++ b/transresults.py Tue Sep 05 21:16:49 2023 +0200 @@ -101,6 +101,8 @@ if self.expr: + store = self.attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" + # Eliminate assignments between constants. if self.static_ref and self.expr.static(): @@ -109,14 +111,14 @@ # Qualified names must be converted into parent-relative assignments. elif self.parent: - return "__store_via_attr_ref(__get_object_attr_ref(&%s, %s), %s)" % ( - encode_path(self.parent), self.attrname, self.expr) + return "%s(__get_object_attr_ref(&%s, %s), %s)" % ( + store, 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_attr(&%s, %s)" % (self.attrname, self.expr) + return "%s(&%s, %s)" % (store, self.attrname, self.expr) # Expressions.