# HG changeset patch # User Paul Boddie # Date 1300658445 -3600 # Node ID 5c0225bbbd03e4881ace1d5a771b0adc5d9c9099 # Parent 96c334ff4431cb3e6bbf5f80b2f8768678d4caa9 Added support in the code generation for class attribute assignment, introducing optimisation-related configuration of the support depending on whether attribute usage is being tracked. Prevented __class__ attribute assignment if detected at compile-time. Added run-time support for class attribute assignment. Added registration of instance attribute usage upon assignment. Added a special attribute to exception instances, protecting it from vacuuming. Fixed a test of class attribute assignment so that it can be run successfully. diff -r 96c334ff4431 -r 5c0225bbbd03 lib/builtins.py --- a/lib/builtins.py Sat Mar 19 22:46:02 2011 +0100 +++ b/lib/builtins.py Sun Mar 20 23:00:45 2011 +0100 @@ -367,6 +367,7 @@ "Implementation of BaseException." def __init__(self, *args): + self._pc = None # remember where the exception occurred self.args = args class Exception(BaseException): pass diff -r 96c334ff4431 -r 5c0225bbbd03 micropython/ast.py --- a/micropython/ast.py Sat Mar 19 22:46:02 2011 +0100 +++ b/micropython/ast.py Sun Mar 20 23:00:45 2011 +0100 @@ -38,10 +38,7 @@ LoadAddress, LoadAddressContext, LoadAddressContextCond, LoadAttr, LoadAttrIndex, LoadAttrIndexContextCond ) - attribute_store_instructions = ( - None, None, None, - StoreAttr, StoreAttrIndex, None - ) + # attribute_store_instructions are defined by the optimiser # Name access instructions, for use with the appropriate handlers. @@ -484,7 +481,7 @@ "Assign the assignment expression to the recipient 'node'." - self._visitAttr(node, self.attribute_store_instructions) + self._visitAttr(node, self.optimiser.get_attribute_store_instructions()) self.set_source() def visitAssList(self, node): diff -r 96c334ff4431 -r 5c0225bbbd03 micropython/data.py --- a/micropython/data.py Sat Mar 19 22:46:02 2011 +0100 +++ b/micropython/data.py Sun Mar 20 23:00:45 2011 +0100 @@ -1226,12 +1226,23 @@ # Administrative methods. def items_for_vacuum(self): + + "Consider both class and instance attributes for vacuuming." + items = [] for name in self.instattr: items.append((name, None)) return NamespaceDict.items_for_vacuum(self) + items def vacuum_item(self, name): + + "Vacuum 'name' from the class or instance attribute collections." + + # NOTE: Hack to prevent damage to exceptions. + + if name == "_pc": + return 0 + if not NamespaceDict.vacuum_item(self, name): self.instattr.remove(name) return 1 diff -r 96c334ff4431 -r 5c0225bbbd03 micropython/inspect.py --- a/micropython/inspect.py Sat Mar 19 22:46:02 2011 +0100 +++ b/micropython/inspect.py Sun Mar 20 23:00:45 2011 +0100 @@ -296,6 +296,7 @@ """ module.set(name, self.expr, 0) + self.use_specific_attribute(module.full_name(), name) def store_class_attr(self, name, cls): @@ -305,6 +306,7 @@ """ cls.set(name, self.expr, 0) + self.use_specific_attribute(cls.full_name(), name) def store_instance_attr(self, name): @@ -315,7 +317,9 @@ # Current namespace is the function. # Previous namespace is the class. - self.namespaces[-2].add_instance_attribute(name) + cls = self.namespaces[-2] + cls.add_instance_attribute(name) + self.use_specific_attribute(cls.full_name(), name) def get_namespace(self): @@ -609,12 +613,10 @@ elif isinstance(value, Module): self.store_module_attr(attrname, value) - self.use_specific_attribute(value.full_name(), attrname) print "Warning: attribute %r of module %r set outside the module." % (node.attrname, expr.get_value().name) elif isinstance(value, Class): self.store_class_attr(attrname, value) - self.use_specific_attribute(value.full_name(), attrname) # Note usage of the attribute where a local is involved. diff -r 96c334ff4431 -r 5c0225bbbd03 micropython/opt.py --- a/micropython/opt.py Sat Mar 19 22:46:02 2011 +0100 +++ b/micropython/opt.py Sun Mar 20 23:00:45 2011 +0100 @@ -57,6 +57,24 @@ self.active_value = None + def get_attribute_store_instructions(self): + + """ + Return an appropriate set of instructions available when storing + attributes. + """ + + ca = self.should_optimise_accesses_by_attribute_usage() + + return ( + ca and StoreAddress or None, # plain assignment + ca and StoreAddressContext or None, # assignment via class + ca and StoreAddressContext or None, # assignment via class (never via instance) + StoreAttr, # assignment via instance + StoreAttrIndex, # special assignment via instance + None + ) + def reset(self): "Reset the optimiser, clearing the active instructions." diff -r 96c334ff4431 -r 5c0225bbbd03 micropython/trans.py --- a/micropython/trans.py Sat Mar 19 22:46:02 2011 +0100 +++ b/micropython/trans.py Sun Mar 20 23:00:45 2011 +0100 @@ -431,8 +431,11 @@ if attrname == "__class__": if isinstance(target, Class): - self.replace_active_value(LoadAddress(self.get_builtin("type", node))) - return + if AddressInstruction is LoadAddress: + self.replace_active_value(LoadAddress(self.get_builtin("type", node))) + return + else: + raise TranslateError("Assigning to __class__ is not permitted.") # Access the object table to get the attribute. diff -r 96c334ff4431 -r 5c0225bbbd03 rsvp.py --- a/rsvp.py Sat Mar 19 22:46:02 2011 +0100 +++ b/rsvp.py Sun Mar 20 23:00:45 2011 +0100 @@ -463,11 +463,7 @@ # Overwrite context if null. context_value = self.value source_value = self.source - if source_value.context is ReplaceableContext: - context = context_value.ref - else: - context = source_value.context - self.save(self.operand, DataValue(context, source_value.ref)) + self._StoreAddressContext(self.operand, context_value, source_value) def MakeInstance(self): size = self.operand @@ -545,8 +541,8 @@ attr_index, static_attr, offset = element if attr_index == self.operand: if static_attr: - self.exception = self._MakeObject(Library.instance_size, self.type_error_instance) - return self.RaiseException() + self._StoreAddressContext(offset, value, self.source) + return else: self.save(value.ref + offset, self.source) return @@ -864,6 +860,13 @@ else: return DataValue(context, ref) + def _StoreAddressContext(self, location, context_value, source_value): + if source_value.context is ReplaceableContext: + context = context_value.ref + else: + context = source_value.context + self.save(location, DataValue(context, source_value.ref)) + # Convenience functions. def machine(program, with_builtins=0, debug=0, abort_upon_exception=0): diff -r 96c334ff4431 -r 5c0225bbbd03 tests/attributes_class_assignment_unknown_alternatives.py --- a/tests/attributes_class_assignment_unknown_alternatives.py Sat Mar 19 22:46:02 2011 +0100 +++ b/tests/attributes_class_assignment_unknown_alternatives.py Sun Mar 20 23:00:45 2011 +0100 @@ -7,16 +7,16 @@ class D: clsattr = 321 -def f(cls): +def f(cls, x): cls.clsattr = 789 - if cls.clsattr: + if x: cls.clsattr2 = 234 -f(C) -f(D) +f(C, 1) +f(D, 0) # prevent AttributeError -result_789 = C.clsattr -result_234 = C.clsattr2 -result_789 = D.clsattr +result1_789 = C.clsattr +result1_234 = C.clsattr2 +result2_789 = D.clsattr # vim: tabstop=4 expandtab shiftwidth=4