# HG changeset patch # User Paul Boddie # Date 1341354449 -7200 # Node ID 7faa219df63741cddeb0276d04b53ef8413b1b80 # Parent 16ae35b97fa36b4a0d2d0c19dcc113141f8c0f27 Switched the order of global statement discovery and module code visiting, changing module attribute initialisation so that global statements are regarded as potential multiple assignments of unknown objects and thus preventing such globals from being treated as sources of constant values. Added modified name registration for module globals so that the combined attribute usage of modified globals can be revoked when finalising the attribute users for a module's top-level code. diff -r 16ae35b97fa3 -r 7faa219df637 TO_DO.txt --- a/TO_DO.txt Tue Jul 03 23:52:27 2012 +0200 +++ b/TO_DO.txt Wed Jul 04 00:27:29 2012 +0200 @@ -76,6 +76,8 @@ types for a name, and the final set of names leading to such type deductions might be a useful annotation to be added alongside _attrcombined. + Update the reports to group identical sets of attribute names. + Interface/Type Generalisation ----------------------------- @@ -230,9 +232,6 @@ Consider a separate annotation phase where deductions are added to the AST for the benefit of both the reporting and code generation phases. -Support self attribute visualisation in the reports and/or provide a function or -annotations which can provide the eventual optimisation directly to such components. - Check context_value initialisation (avoiding or handling None effectively). Consider better "macro" support where new expressions need to be generated and processed. diff -r 16ae35b97fa3 -r 7faa219df637 micropython/data.py --- a/micropython/data.py Tue Jul 03 23:52:27 2012 +0200 +++ b/micropython/data.py Wed Jul 04 00:27:29 2012 +0200 @@ -2015,6 +2015,13 @@ self.all_objects = set() + # A dictionary mapping functions to global names that cannot support + # combined attribute usage observations because they may be modified in + # the given functions during initialisation. Functions could be + # eliminated from this dictionary if not thought to be used. + + self.modified_names = {} + # Keyword records. self.keyword_names = set() @@ -2055,6 +2062,83 @@ return dict(self) + # Attribute usage methods that apply to module globals in certain + # circumstances. + + def finalise_users(self, objtable): + + """ + Finalise attribute users, first revoking usage for any global names + that might be modified during initialisation. + """ + + NamespaceDict.finalise_users(self, objtable) + + # Collect modified names and revoke attribute usage details for those + # names. + + names = set() + for function_names in self.modified_names.values(): + names.update(function_names) + + self.revoke_attribute_usage(names) + + def revoke_attribute_usage(self, names): + + """ + The definition of attribute users may be overridden or ignored if the + module is found to be involved in circular imports where externally + initiated modification occurs or if a function in the module may rebind + globals, potentially during initialisation. + """ + + for user in self.all_attribute_users: + + # Remove user details for each name. + + for name in names: + + # Remove annotations derived from the combined observations. + + if user._attrtypes.has_key(name): + del user._attrtypes[name] + if user._attrspecifictypes.has_key(name): + del user._attrspecifictypes[name] + if user._attrmerged.has_key(name): + del user._attrmerged[name] + + # Usage nodes that can no longer support combined observations + # of attribute usage must instead support isolated observations. + + if not user._attrcombined.has_key(name): + continue + + usage = user._attrcombined[name] + + # Usage is defined as a collection of attribute name + # observations. + + for attrnames in usage: + + # Each attribute name may have values associated with it + # where such values have been assigned to an attribute of + # the given name. + + for attrname, attrvalues in usage.items(): + if attrvalues: + for value in attrvalues: + self.importer.use_name(attrname, self.full_name(), value) + else: + self.importer.use_name(attrname, self.full_name()) + + del user._attrcombined[name] + + # Remove annotations on contributors. + + for contributor in user._attrcontributors: + if contributor._attrusers.has_key(name): + del contributor._attrusers[name] + # Pre-made instances. type_class = TypeClass("type") # details to be filled in later diff -r 16ae35b97fa3 -r 7faa219df637 micropython/inspect.py --- a/micropython/inspect.py Tue Jul 03 23:52:27 2012 +0200 +++ b/micropython/inspect.py Wed Jul 04 00:27:29 2012 +0200 @@ -85,6 +85,16 @@ """ An inspected module, providing core details via the Module superclass, but capable of being used as an AST visitor. + + A module can be inspected through the invocation of the following methods in + order: + + 1. parse + 2. process_functions + 3. vacuum + 4. finalise + + A module importer can be expected to perform these invocations. """ def __init__(self, name, importer): @@ -138,16 +148,14 @@ self.store("__name__", self._visitConst(self.full_name())) - # First, visit module-level code, recording global names. + # Detect and record globals declared in the module. + + self.process_globals(module) + + # Visit module-level code, also recording global names. processed = self.dispatch(module) - # Then, for each function, detect and record globals declared in those - # functions. - - for node, namespaces in self.functions: - self.process_globals(node) - self.finalise_attribute_usage() # Add references to other modules declared using the __all__ global. @@ -186,8 +194,12 @@ for n in node.getChildNodes(): if isinstance(n, compiler.ast.Global): for name in n.names: - if not self.has_key(name): - self[name] = make_instance() + + # Each name may potentially be assigned many times. + # NOTE: We don't try and find out the specifics at this + # NOTE: point and just set an instance instead. + + self.set(name, make_instance(), False) else: self.process_globals(n) @@ -1067,6 +1079,19 @@ # The name is recorded in an earlier process. + # Since the presence of a global declaration may + # indicate the modification of a name, and this may + # happen during initialisation, the name is recorded + # and any usage observations ultimately revoked. + + # NOTE: Actual modification could be detected by + # NOTE: establishing a path from the module top-level to + # NOTE: an actual assignment. + + if not self.modified_names.has_key(ns): + self.modified_names[ns] = set() + self.modified_names[ns].add(name) + def visitIf(self, node): self.use_name("__bool__", node) self.new_branchpoint() diff -r 16ae35b97fa3 -r 7faa219df637 tests/changed_globals_function.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/changed_globals_function.py Wed Jul 04 00:27:29 2012 +0200 @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +class C: + p = 123 + +class D: + p = 456 + +x = C + +def change(): + global x + x = D + +def f(): + return x.p + +change() +result1_456 = x.p +result2_456 = f() + +# vim: tabstop=4 expandtab shiftwidth=4