# HG changeset patch # User Paul Boddie # Date 1237500355 -3600 # Node ID 6f6376544ce5d90feab10f377adac2bb46ffacb8 # Parent ad0b88c81a1f202a199b9edc23022be3cef970e0 Introduced new instruction definitions which represent different access conditions, although not all are currently in use. Removed the context override flag from the object list. Introduced breakpoint support in the RSVP interpreter. Made the working of the attributes test more open to inspection. diff -r ad0b88c81a1f -r 6f6376544ce5 docs/assignment.txt --- a/docs/assignment.txt Thu Mar 05 00:45:42 2009 +0100 +++ b/docs/assignment.txt Thu Mar 19 23:05:55 2009 +0100 @@ -42,18 +42,16 @@ Access types: - Access to stored value from... Effect on context Optimised instruction Unoptimised instruction - ------------------------------ ----------------- --------------------- ----------------------- + Access to stored value from... Effect on context Optimised instruction Unoptimised instruction + ------------------------------ ----------------- --------------------- ----------------------- local preserved LoadName - global (module) preserved LoadAddress LoadAttrIndex - class preserved LoadAddress LoadAttrIndex - class via instance overridden LoadAddressContext (*) LoadAttrIndex (*) - instance preserved LoadAttr LoadAttrIndex + global (module) preserved LoadAddress LoadAttrIndex + class preserved LoadAddress LoadAttrIndex + class via instance overridden LoadAddressContext(Cond) LoadAttrIndexContext(Cond) + instance preserved LoadAttr LoadAttrIndex Access to a namespace may not preserve the stored context - (*) Other modes of these instructions may exist for certain optimisations - Access to class attributes via instances: Access to stored value with... Effect on context @@ -63,21 +61,18 @@ null context preserved other context (instance) preserved - LoadAddressContext and LoadAttrIndex must therefore check whether the - context must be overridden - -Optimisation possibilities: +Optimisation possibilities for class attribute access via instances: Class Class attribute Context of attribute Instruction ----- --------------- -------------------- ----------- known constant preserved LoadAddress known constant overridden LoadAddressContext - known not constant preserved LoadAddress (attribute may always be preserved) - known not constant overridden LoadAddressContext (attribute may always be overridden) - known not constant not known LoadAddressContextCond - not known not known preserved LoadAttrIndex (attribute may have preserved context in all classes) - not known not known overridden LoadAttrIndexContext (attribute may have overridden context in all classes) - not known not known not known LoadAttrIndexContextCond + known not constant preserved LoadAddress (attribute may always be preserved) + known not constant overridden LoadAddressContext (attribute may always be overridden) + known not constant not known LoadAddressContextCond (perform context check) + not known not known preserved LoadAttrIndex (attribute may have preserved context in all classes) + not known not known overridden LoadAttrIndexContext (attribute may have overridden context in all classes) + not known not known not known LoadAttrIndexContextCond (perform context check for class attribute access) Since the object table encodes sufficient information (an instance must be compatible to access the class attribute, and compatibility information is diff -r ad0b88c81a1f -r 6f6376544ce5 micropython/ast.py --- a/micropython/ast.py Thu Mar 05 00:45:42 2009 +0100 +++ b/micropython/ast.py Thu Mar 19 23:05:55 2009 +0100 @@ -34,8 +34,14 @@ # Attribute access instructions, for use with the appropriate handlers. - attribute_load_instructions = (LoadAddress, LoadAddressContext, LoadAttr, LoadAttrIndex) - attribute_store_instructions = (None, None, StoreAttr, StoreAttrIndex) + attribute_load_instructions = ( + LoadAddress, LoadAddressContext, LoadAddressContextCond, + LoadAttr, LoadAttrIndex, LoadAttrIndexContext, LoadAttrIndexContextCond + ) + attribute_store_instructions = ( + None, None, None, + StoreAttr, StoreAttrIndex, None, None + ) # Name access instructions, for use with the appropriate handlers. @@ -474,7 +480,9 @@ Generate code for the access to 'attrname' using the given 'classes'. """ - AddressInstruction, AddressContextInstruction, AttrInstruction, AttrIndexInstruction = classes + AddressInstruction, AddressContextInstruction, AddressContextCondInstruction, \ + AttrInstruction, AttrIndexInstruction, AttrIndexContextInstruction, \ + AttrIndexContextCondInstruction = classes # Where the last operation (defining the attribute owner) yields a # constant... @@ -482,6 +490,8 @@ target_name = self.optimiser.optimise_constant_accessor() # Only try and discover the position if the target can be resolved. + # Since instances cannot be constants, this involves classes and + # modules. if target_name is not None: @@ -513,6 +523,10 @@ # see if the attribute is acceptably positioned and produce a direct # access to the attribute. + # This is the only reliable way of detecting instance accesses at + # compile-time since in general, objects could be classes or modules, + # but 'self' should only refer to instances. + elif self.optimiser.optimise_self_access(self.unit, attrname): # Either generate an instruction operating on an instance attribute. @@ -546,13 +560,28 @@ # Preserve the context if the class attribute comes from an # incompatible class. - else: + elif attr.defined_outside_hierarchy(): + + # Only permit loading (not storing) of class attributes via self. + if AddressInstruction is not None: self.new_op(AddressInstruction(attr)) else: raise TranslateError(self.module.full_name(), node, "Storing of class attribute %r via self not permitted." % attrname) + # Otherwise, test for a suitable context at run-time. + + else: + + # Only permit loading (not storing) of class attributes via self. + + if AddressContextCondInstruction is not None: + self.new_op(AddressContextCondInstruction(attr)) + else: + raise TranslateError(self.module.full_name(), node, + "Storing of class attribute %r via self not permitted." % attrname) + return # Or delegate the attribute access to a general instruction @@ -570,7 +599,16 @@ raise TranslateError(self.module.full_name(), node, "No attribute entry exists for name %r." % attrname) - self.new_op(AttrIndexInstruction(index)) + # NOTE: Test for class vs. instance attributes, generating + # NOTE: context-related instructions. + + if AttrIndexContextCondInstruction is not None: + self.new_op(AttrIndexContextCondInstruction(index)) + + # Store instructions do not need to consider context modifications. + + else: + self.new_op(AttrIndexInstruction(index)) # Invocations involve the following: # diff -r ad0b88c81a1f -r 6f6376544ce5 micropython/data.py --- a/micropython/data.py Thu Mar 05 00:45:42 2009 +0100 +++ b/micropython/data.py Thu Mar 19 23:05:55 2009 +0100 @@ -298,6 +298,37 @@ else: return 0 + def defined_outside_hierarchy(self): + + """ + Return whether the parent and context of the attribute never belong to + the same class hierarchy. + """ + + # Must be defined within a class. + + if isinstance(self.parent, Class): + + # To be sure, all contexts must be classes and be the same as the + # parent, or be a superclass of the parent, or be a subclass of the + # parent. + + for context in self.get_contexts(): + if not ( + isinstance(context, Class) and not ( + context is self.parent or + context.has_subclass(self.parent) or + self.parent.has_subclass(context)) + ): + return 0 + + return 1 + + # Instance attributes are not defined within a hierarchy. + + else: + return 0 + def __repr__(self): return "Attr(%r, %s, %r) # [%s], %r" % ( self.position, shortrepr(self.parent), self.name, diff -r ad0b88c81a1f -r 6f6376544ce5 micropython/opt.py --- a/micropython/opt.py Thu Mar 05 00:45:42 2009 +0100 +++ b/micropython/opt.py Thu Mar 19 23:05:55 2009 +0100 @@ -178,7 +178,9 @@ return isinstance(instruction, ( StoreTemp, StoreFrame, StoreResult, StoreException, # as the value being stored - LoadAddressContext, LoadAttr, LoadAttrIndex, # as the object being referenced + LoadAddressContext, LoadAddressContextCond, # as the object being referenced + LoadAttr, LoadAttrIndex, LoadAttrIndexContext, # as the object being referenced + LoadAttrIndexContextCond, # as the object being referenced StoreAttr, StoreAttrIndex, StoreCallable, # as the object being referenced StoreFrameIndex, # as the object being referenced LoadCallable, diff -r ad0b88c81a1f -r 6f6376544ce5 micropython/rsvp.py --- a/micropython/rsvp.py Thu Mar 05 00:45:42 2009 +0100 +++ b/micropython/rsvp.py Thu Mar 19 23:05:55 2009 +0100 @@ -156,6 +156,9 @@ class LoadAddress(Address): "Load the current value from the given fixed attribute address." class StoreAddress(Address): "Store the source value into the given fixed attribute address." class LoadAddressContext(Address): "Load the current value from the given fixed attribute address, making the current value the context." +class LoadAddressContextCond(Address): + """Load the current value from the given fixed attribute address, only making the current value the + context if the attribute is compatible.""" class MakeObject(Immediate): "Make a new object. There isn't a complementary DropObject." # Access to address-relative data. @@ -164,6 +167,12 @@ class StoreAttr(AR): "Store the source value into the given attribute of the object referenced by the current value." class LoadAttrIndex(Immediate): "Load into the current value the attribute of the current value with the given index." class StoreAttrIndex(Immediate): "Store the source value into the attribute of the current value with the given index." +class LoadAttrIndexContext(Immediate): + """Load into the current value the attribute of the current value with the given index, making the + current value the context.""" +class LoadAttrIndexContextCond(Immediate): + """Load into the current value the attribute of the current value with the given index, only making the + current value the context if the attribute is compatible.""" # Access to object details. @@ -214,8 +223,10 @@ # Instructions which affect the current value. current_value_instructions = ( - LoadConst, LoadName, LoadTemp, LoadAddress, LoadAddressContext, - LoadAttr, LoadAttrIndex, LoadCallable, LoadContext, LoadResult, + LoadConst, LoadName, LoadTemp, + LoadAddress, LoadAddressContext, LoadAddressContextCond, + LoadAttr, LoadAttrIndex, LoadAttrIndexContext, LoadAttrIndexContextCond, + LoadCallable, LoadContext, LoadResult, LoadException, MakeObject ) diff -r ad0b88c81a1f -r 6f6376544ce5 micropython/table.py --- a/micropython/table.py Thu Mar 05 00:45:42 2009 +0100 +++ b/micropython/table.py Thu Mar 19 23:05:55 2009 +0100 @@ -173,7 +173,7 @@ # Support descendants. if isinstance(attr, Class): - return (attr_index, None, None, None) + return (attr_index, None, None) if attr.parent is not None: location = attr.parent.location or 0 @@ -184,9 +184,9 @@ else: position = None # NOTE: Should fix unpositioned attributes. - # Attribute index/code, attribute type, context instance override flag, location/position. + # Attribute index/code, attribute type, location/position. - return (attr_index, attr.is_class_attribute(), attr.defined_within_hierarchy(), position) + return (attr_index, attr.is_class_attribute(), position) class ParameterList(List): diff -r ad0b88c81a1f -r 6f6376544ce5 rsvp.py --- a/rsvp.py Thu Mar 05 00:45:42 2009 +0100 +++ b/rsvp.py Thu Mar 19 23:05:55 2009 +0100 @@ -71,6 +71,9 @@ class EmptyFrameStack(Exception): pass +class BreakpointReached(Exception): + pass + class RSVPMachine: "A really simple virtual processor." @@ -119,6 +122,10 @@ cls = objlist.access("__builtins__", "int") self.int_instance_location = cls and cls.get_value() and cls.get_value().instance_template_location + # Debugging attributes. + + self.breakpoints = set() + # Debugging methods. def dump(self): @@ -164,6 +171,9 @@ if dump: self.dump() + def set_break(self, location): + self.breakpoints.add(location) + # Internal operations. def load(self, address): @@ -228,6 +238,10 @@ "Execute code in the memory at the current PC address." + if self.pc in self.breakpoints: + self.breakpoints.remove(self.pc) + raise BreakpointReached + self.instruction = self.load(self.pc) # Process any inputs of the instruction. @@ -322,7 +336,12 @@ def LoadAddressContext(self): context, ref = self.load(self.operand) inst_context, inst_ref = self.value - self.value = self._LoadAddressContext(context, ref, inst_context, inst_ref) + self.value = inst_ref, ref + + def LoadAddressContextCond(self): + context, ref = self.load(self.operand) + inst_context, inst_ref = self.value + self.value = self._LoadAddressContextCond(context, ref, inst_context, inst_ref) def StoreAddress(self): # Preserve context. @@ -352,22 +371,50 @@ context, ref = self.value data = self.load(ref) element = self.objlist[data.classcode + self.operand] - attr_index, class_attr, replace_context, offset = element + attr_index, class_attr, offset = element if attr_index == self.operand: if class_attr: - loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute - self.value = self._LoadAddressContext(loaded_context, loaded_ref, context, ref) + self.value = self.load(offset) # offset is address of class attribute else: self.value = self.load(ref + offset) else: self.exception = self.attr_error return self.RaiseException() + def LoadAttrIndexContext(self): + context, ref = self.value + data = self.load(ref) + element = self.objlist[data.classcode + self.operand] + attr_index, class_attr, offset = element + if attr_index == self.operand: + loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute + self.value = ref, loaded_ref + else: + self.exception = self.attr_error + return self.RaiseException() + + def LoadAttrIndexContextCond(self): + context, ref = self.value + data = self.load(ref) + element = self.objlist[data.classcode + self.operand] + attr_index, class_attr, offset = element + if attr_index == self.operand: + if class_attr: + loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute + self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, context, ref) + else: + self.value = self.load(ref + offset) + else: + self.exception = self.attr_error + return self.RaiseException() + + # NOTE: LoadAttrIndexContextCond will test context compatibility. + def StoreAttrIndex(self): context, ref = self.value data = self.load(ref) element = self.objlist[data.classcode + self.operand] - attr_index, class_attr, replace_context, offset = element + attr_index, class_attr, offset = element if attr_index == self.operand: if class_attr: self.exception = self.type_error @@ -554,7 +601,7 @@ # Find the table entry for the descendant. element = self.objlist[target_data.classcode + data.attrcode] - attr_index, class_attr, replace_context, offset = element + attr_index, class_attr, offset = element return attr_index == data.attrcode def _MakeObject(self, size, ref): @@ -564,7 +611,7 @@ self.save(addr, data) return addr - def _LoadAddressContext(self, context, ref, inst_context, inst_ref): + def _LoadAddressContextCond(self, context, ref, inst_context, inst_ref): # Check the instance context against the target's context. diff -r ad0b88c81a1f -r 6f6376544ce5 tests/attributes.py --- a/tests/attributes.py Thu Mar 05 00:45:42 2009 +0100 +++ b/tests/attributes.py Thu Mar 19 23:05:55 2009 +0100 @@ -12,7 +12,8 @@ self.attr = value C.clsattr -C -C.clsattr +a = C.clsattr +c = C(789) +c.update(987) # vim: tabstop=4 expandtab shiftwidth=4