# HG changeset patch # User Paul Boddie # Date 1258859705 -3600 # Node ID 50008931d30405cf731057cd7867e600ec103d0f # Parent dd6568a7c46e5f72c4971b64a7aaf89483c8af8e Added incomplete work to introduce attribute deletion and object table simplification based on attribute usage, introducing specific object traversal to detect used attributes instead of considering usage of individual names and then traversing all objects with such names. Updated the test program to properly handle translation exceptions, fixing the representation of exceptions for AST nodes without line numbers. diff -r dd6568a7c46e -r 50008931d304 micropython/__init__.py --- a/micropython/__init__.py Mon Nov 16 00:34:47 2009 +0100 +++ b/micropython/__init__.py Sun Nov 22 04:15:05 2009 +0100 @@ -22,12 +22,10 @@ -------- -To use this module, an importer should be constructed and the load_from_file -method used. Here, the standard path for module searching is employed: +To use this module, an importer should be constructed. Here, the standard path +for module searching is employed: importer = Importer(sys.path) -importer.load_from_file(filename) -importer.vacuum() To generate programs, the above importer should be supplied in the initialisation of a program instance, and then various methods are called: @@ -98,6 +96,14 @@ objtable = self.get_object_table() paramtable = self.get_parameter_table() + + # Optimise and regenerate the object table. + + self.importer.vacuum(objtable) + objtable = self.get_object_table(reset=1) + + # Finalise the importer. + self.importer.finalise() self.code = [] @@ -243,12 +249,11 @@ self.code_location = self.importer.modules["__main__"].code_location return self.raw_code - def get_object_table(self): + def get_object_table(self, reset=0): "Return a table with details of attributes for classes and modules." - if self.objtable is None: - self.importer.vacuum() + if self.objtable is None or reset: t = self.objtable = micropython.table.ObjectTable() for module in self.importer.get_modules(): @@ -331,6 +336,10 @@ "NotImplemented" : NotImplemented } + names_always_used = [ + "bool", "__init__", "__call__", "__bool__", "__builtins__", "__main__" + ] + def __init__(self, path=None, verbose=0, optimisations=None): """ @@ -358,11 +367,9 @@ self.constant_list = None # cache for constants self.init_predefined_constants() - # Name records (used to track actual use of names). - # Include names which may not be explicitly used in programs. - # NOTE: Potentially declare these when inspecting. + # Attribute usage. - self.names_used = None + self.attributes_used = None self.name_references = {} # Status information. @@ -384,13 +391,17 @@ # General maintenance. - def vacuum(self): + def vacuum(self, objtable): "Tidy up the modules." if self.vacuumed: return + # Complete the list of attribute names used in the program. + + self.collect_attributes(objtable) + for name, module in self.modules.items(): if module.loaded: module.vacuum() @@ -401,13 +412,11 @@ def finalise(self): - "Finalise the program." + "Finalise the program (which should have been vacuumed first)." if self.finalised: return - self.vacuum() - for module in self.get_modules(): module.finalise() @@ -424,24 +433,73 @@ if not self.name_references.has_key(from_name): self.name_references[from_name] = set() - self.name_references[from_name].add(name) + self.name_references[from_name].add((name,)) + + def use_names(self, names, from_name): + + """ + Register the given 'names' as being used in the program from within an + object with the specified 'from_name'. + """ + + if not self.name_references.has_key(from_name): + self.name_references[from_name] = set() + self.name_references[from_name].add(names) + + def uses_attribute(self, obj, name): - def uses_name(self, name): + """ + Return whether the attribute of object 'obj' having the given 'name' is + used as an attribute in the program. + """ + + return name in self.attributes_used.get(obj.full_name(), []) + + def use_attribute(self, objname, name): - "Return whether the given 'name' is used." + """ + Indicate that in the object with the given 'objname', the attribute of + the given 'name' is used. + """ + + if not self.attributes_used.has_key(objname): + self.attributes_used[objname] = set() + self.attributes_used[objname].add(name) - if self.names_used is None: - self.names_used = set(["bool", "__init__", "__call__", "__bool__", "__builtins__", "__main__"]) - self._collect_names("__builtins__") - self._collect_names("__main__") + def collect_attributes(self, objtable): + + "Collect attribute references for the entire program." + + if self.attributes_used is None: + + # Include names which may not be explicitly used in programs. + # NOTE: Potentially declare these when inspecting. + + self.attributes_used = {} + for name in self.names_always_used: + for objname in objtable.all_possible_objects([name]): + self.use_attribute(objname, name) + + # Start with the "root" modules, finding referenced objects. - return name in self.names_used + self._collect_attributes("__builtins__", objtable) + self._collect_attributes("__main__", objtable) + + def _collect_attributes(self, from_name, objtable): - def _collect_names(self, from_name): - for name in self.name_references.get(from_name, []): - if name not in self.names_used: - self.names_used.add(name) - self._collect_names(name) + """ + Given an object called 'from_name', find all names referenced from such + an object according to the register of names. + """ + + if self.attributes_used.has_key(from_name): + return + + for names in self.name_references.get(from_name, []): + for objname in objtable.all_possible_objects(names): + for name in names: + self.use_attribute(objname, name) + self._collect_attributes(objname + "." + name, objtable) # Constant accounting. diff -r dd6568a7c46e -r 50008931d304 micropython/common.py --- a/micropython/common.py Mon Nov 16 00:34:47 2009 +0100 +++ b/micropython/common.py Sun Nov 22 04:15:05 2009 +0100 @@ -60,7 +60,7 @@ self.message = message def __repr__(self): - return "Error in %r at line %d: %s" % (self.unit_name, self.astnode.lineno, self.message) + return "Error in %r at line %r: %s" % (self.unit_name, self.astnode.lineno, self.message) def __str__(self): return repr(self) diff -r dd6568a7c46e -r 50008931d304 micropython/data.py --- a/micropython/data.py Mon Nov 16 00:34:47 2009 +0100 +++ b/micropython/data.py Sun Nov 22 04:15:05 2009 +0100 @@ -121,10 +121,12 @@ # Attributes accessed on objects, potentially narrowing their types. # Specific namespaces should define known names during initialisation. + # For example, functions can define names belonging to parameters. self.attributes_used = [{}] # stack of usage self.attribute_shelves = [] # stack of unmerged definitions self.attribute_users = [{}] # stack of assignments + self.all_attributes_used = [] # Attribute/name definition and access. @@ -278,10 +280,46 @@ # Attribute usage methods. - def _use_attribute(self, attr, attrname): - name = attr.name + def get_active_attributes(self): + + """ + Return attributes on this object that are actually used. + """ + + return self.attributes_used[-1].get(None, []) + + def get_all_attribute_usage(self): + + """ + Return a set of all usage tuples for attributes used with the different + local names. + """ + + usage = set() + for names in self.all_attributes_used: + usage.add(tuple(names)) + return usage + + # These shadow various methods in the InspectedModule class, and provide + # implementations generally. + + def _use_attribute(self, name, attrname): + + """ + Indicate the use on the given 'name' in this namespace of an attribute + with the given 'attrname'. + """ + defs = self.attributes_used[-1] users = self.attribute_users[-1] + + # Permit the unforeseen definition of names since some names are likely + # to be defined by things (such as class definitions) which will not + # require access optimisations, but which still need to be monitored. + + if not defs.has_key(name): + defs[name] = set() + defs[name].add(attrname) return defs[name] @@ -296,14 +334,24 @@ self._define_attribute_user_for_name(node, name) def _define_attribute_user_for_name(self, node, name): + + "Define 'node' as the user of attributes for the given 'name'." + defs = self.attributes_used[-1] users = self.attribute_users[-1] users[name] = node defs[name] = set() + + # Record the attribute combinations for the name. + if not hasattr(node, "_attrnames"): node._attrnames = {} node._attrnames[name] = defs[name] + # Remember all attribute combinations. + + self.all_attributes_used.append(defs[name]) + def _reset_all_attributes(self): self.attributes_used[-1] = {} self.attribute_users[-1] = {} diff -r dd6568a7c46e -r 50008931d304 micropython/inspect.py --- a/micropython/inspect.py Mon Nov 16 00:34:47 2009 +0100 +++ b/micropython/inspect.py Sun Nov 22 04:15:05 2009 +0100 @@ -151,6 +151,9 @@ for node, namespaces in self.functions: self._visitFunctionBody(node, namespaces) + self.finalise_namespace(namespaces[-1]) + + self.finalise_namespace(self) # Add references to other modules declared using the __all__ global. @@ -177,6 +180,15 @@ else: self.process_globals(n) + def finalise_namespace(self, namespace): + + "Finalise the given 'namespace'." + + for names in namespace.get_all_attribute_usage(): + self.importer.use_names(names, namespace.full_name()) + for name in namespace.get_active_attributes(): + self.importer.use_name(name, namespace.full_name()) + def vacuum(self): """ @@ -202,7 +214,8 @@ # Only consider deleting entire unused objects or things accessible # via names which are never used. - if delete_all or not self.importer.uses_name(name): + if delete_all or not self.importer.uses_attribute(obj, name): + print "Deleting", obj.full_name(), name del obj[name] # Delete any unambiguous attribute value. Such values can only @@ -304,7 +317,7 @@ "Use the given 'name' within the current namespace/unit." unit = self.get_namespace() - self.importer.use_name(name, unit.name) + self.importer.use_name(name, unit.full_name()) # Attribute usage methods. # These are convenience methods which refer to the specific namespace's @@ -334,8 +347,8 @@ def reset_all_attributes(self): self.get_namespace()._reset_all_attributes() - def use_attribute(self, attr, attrname): - return self.get_namespace()._use_attribute(attr, attrname) + def use_attribute(self, name, attrname): + return self.get_namespace()._use_attribute(name, attrname) # Visitor methods. @@ -469,7 +482,7 @@ # Note usage of the attribute. if expr.parent is self.get_namespace(): - node._attrnames = self.use_attribute(expr, node.attrname) + node._attrnames = self.use_attribute(expr.name, node.attrname) return None @@ -492,7 +505,7 @@ self.store(node.name, self.expr) self.define_attribute_user(node) - self.use_name(node.name) + self.use_attribute(None, node.name) # ensure the presence of the given name return None visitAssTuple = visitAssList @@ -717,16 +730,17 @@ # Note usage of the attribute. if expr.parent is self.get_namespace(): - node._attrnames = self.use_attribute(expr, attrname) + node._attrnames = self.use_attribute(expr.name, attrname) + else: + self.use_name(attrname) elif self.builtins is not None: attr = self.builtins.get(attrname) + self.use_name(attrname) + else: attr = UnresolvedName(attrname, value.full_name(), self) - - # Accounting. - - self.use_name(attrname) + self.use_name(attrname) return attr @@ -811,18 +825,44 @@ def visitName(self, node): name = node.name + # Constants. + if self.importer.predefined_constants.has_key(name): attr = self.importer.get_predefined_constant(name) + + # Locals. + elif self.namespaces and self.namespaces[-1].has_key(name): attr = self.namespaces[-1][name] + + # Note usage of the local (potentially a class attribute). + + node._attrnames = self.use_attribute(None, name) + + # Globals. + elif self.has_key(name): attr = self[name] + + # Note usage of the module attribute. + + node._attrnames = self.use_attribute(None, name) + + # Builtins. + elif self.builtins is not None and self.builtins.has_key(name): attr = self.builtins[name] + + # Note usage of the module attribute. + + node._attrnames = self.builtins.use_attribute(None, name) + + # Unknown. + else: attr = None + self.use_name(name) - self.use_name(name) return attr visitNot = OP diff -r dd6568a7c46e -r 50008931d304 test.py --- a/test.py Mon Nov 16 00:34:47 2009 +0100 +++ b/test.py Sun Nov 22 04:15:05 2009 +0100 @@ -37,7 +37,7 @@ else: filename = None - # Make the program. + # Load the program. try: p = micropython.cmd.get_program(filename, path, args) @@ -47,16 +47,8 @@ else: m = p.get_importer().get_module("__main__") - # Report any errors. + # Build the program. - except micropython.ProcessingError, exc: - print repr(exc) - if "-tb" in args: - raise - - # Provide information to any interactive session. - - else: ot = p.get_object_table() pt = p.get_parameter_table() @@ -66,4 +58,11 @@ success = rm.test(m) print "Test successful?", success + # Report any errors. + + except micropython.ProcessingError, exc: + print repr(exc) + if "-tb" in args: + raise + # vim: tabstop=4 expandtab shiftwidth=4