# HG changeset patch # User Paul Boddie # Date 1476476205 -7200 # Node ID bfde84465ea3a482c9f4e0eb807a4c08d0133afd # Parent 5782a5f685ef73b17b0c182c535ae8a800004069 Added proper support for general attribute assignment. diff -r 5782a5f685ef -r bfde84465ea3 deducer.py --- a/deducer.py Fri Oct 14 21:32:37 2016 +0200 +++ b/deducer.py Fri Oct 14 22:16:45 2016 +0200 @@ -1758,6 +1758,7 @@ preference to the initial accessor) * attributes needing to be traversed from the base that yield unambiguous objects + * access modes for each of the unambiguously-traversed attributes * remaining attributes needing to be tested and traversed * details of the context * the method of obtaining the final attribute @@ -1893,20 +1894,24 @@ # Determine the method of access. + is_assignment = location in self.reference_assignments + # Identified attribute that must be accessed via its parent. - if attr and attr.get_name() and location in self.reference_assignments: - final_method = "assign"; origin = attr.get_name() + if attr and attr.get_name() and is_assignment: + final_method = "static-assign"; origin = attr.get_name() # Static, identified attribute. elif attr and attr.static(): - final_method = "static"; origin = attr.final() + final_method = is_assignment and "static-assign" or "static" + origin = attr.final() # All other methods of access involve traversal. else: - final_method = "access"; origin = None + final_method = is_assignment and "assign" or "access" + origin = None # First attribute accessed at a known position via the accessor. diff -r 5782a5f685ef -r bfde84465ea3 optimiser.py --- a/optimiser.py Fri Oct 14 21:32:37 2016 +0200 +++ b/optimiser.py Fri Oct 14 22:16:45 2016 +0200 @@ -363,17 +363,29 @@ attrname = attrnames[0] del attrnames[0] - access_first_attribute = final_method == "access" or traversed or attrnames + # Perform the first access explicitly if at least one operation + # requires it. + + access_first_attribute = final_method in ("access", "assign") or traversed or attrnames + + # Determine whether the first access involves assignment. + + assigning = not traversed and not attrnames and final_method == "assign" # Set the context if already available. + # Assigning does not set the context. - if context == "original-accessor": - emit(("set_context", original_accessor)) - accessor = "context" - elif context == "base": - emit(("set_context", base)) - accessor = "context" - elif context == "final-accessor" or access_first_attribute: + if not assigning: + if context == "original-accessor": + emit(("set_context", original_accessor)) + accessor = "context" + elif context == "base": + emit(("set_context", base)) + accessor = "context" + elif context == "final-accessor" or access_first_attribute: + emit(("set_accessor", original_accessor)) + accessor = "accessor" + else: emit(("set_accessor", original_accessor)) accessor = "accessor" @@ -398,17 +410,40 @@ if access_first_attribute: if first_method == "relative-class": - emit(("set_accessor", ("load_via_class", accessor, attrname))) + if assigning: + emit(("store_via_class", accessor, attrname, "")) + else: + emit(("set_accessor", ("load_via_class", accessor, attrname))) + elif first_method == "relative-object": - emit(("set_accessor", ("load_via_object", accessor, attrname))) + if assigning: + emit(("store_via_object", accessor, attrname, "")) + else: + emit(("set_accessor", ("load_via_object", accessor, attrname))) + elif first_method == "relative-object-class": - emit(("set_accessor", ("get_class_and_load", accessor, attrname))) + if assigning: + emit(("get_class_and_store", accessor, attrname, "")) + else: + emit(("set_accessor", ("get_class_and_load", accessor, attrname))) + elif first_method == "check-class": - emit(("set_accessor", ("check_and_load_via_class", accessor, attrname))) + if assigning: + emit(("check_and_store_via_class", accessor, attrname, "")) + else: + emit(("set_accessor", ("check_and_load_via_class", accessor, attrname))) + elif first_method == "check-object": - emit(("set_accessor", ("check_and_load_via_object", accessor, attrname))) + if assigning: + emit(("check_and_store_via_object", accessor, attrname, "")) + else: + emit(("set_accessor", ("check_and_load_via_object", accessor, attrname))) + elif first_method == "check-object-class": - emit(("set_accessor", ("check_and_load_via_any", accessor, attrname))) + if assigning: + emit(("check_and_store_via_any", accessor, attrname, "")) + else: + emit(("set_accessor", ("check_and_load_via_any", accessor, attrname))) # Obtain an accessor. @@ -416,39 +451,52 @@ 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 context == "final-accessor": + if remaining == 1 and final_method != "assign" and context == "final-accessor": emit(("set_context", "accessor")) # Perform the access only if not achieved directly. - if remaining > 1 or final_method == "access": + if remaining > 1 or final_method in ("access", "assign"): + if traversal_mode == "class": - emit(("set_accessor", ("load_via_class", "accessor", attrname))) + if assigning: + emit(("store_via_class", "accessor", attrname, "")) + else: + emit(("set_accessor", ("load_via_class", "accessor", attrname))) else: - emit(("set_accessor", ("load_via_object", "accessor", attrname))) + if assigning: + emit(("store_via_object", "accessor", attrname, "")) + else: + emit(("set_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 context == "final-accessor": + if remaining == 1 and final_method != "assign" and context == "final-accessor": emit(("set_context", "accessor")) # Perform the access only if not achieved directly. - if remaining > 1 or final_method == "access": - emit(("set_accessor", ("check_and_load_via_any", "accessor", attrname))) + if remaining > 1 or final_method in ("access", "assign"): + + if assigning: + emit(("check_and_store_via_any", "accessor", attrname, "")) + else: + emit(("set_accessor", ("check_and_load_via_any", "accessor", attrname))) remaining -= 1 - if final_method == "assign": - emit(("store_member", origin, "")) + if final_method == "static-assign": + emit(("store_member", origin, "")) elif final_method == "static": emit(("load_static", origin))