1.1 --- a/micropython/common.py Fri Apr 19 23:38:12 2013 +0200
1.2 +++ b/micropython/common.py Sat Apr 20 00:59:58 2013 +0200
1.3 @@ -3,7 +3,7 @@
1.4 """
1.5 Common classes.
1.6
1.7 -Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -21,6 +21,7 @@
1.13
1.14 from compiler.ast import AssAttr, Getattr, Name
1.15 import compiler.ast
1.16 +from micropython.basicdata import Const, Constant
1.17 from micropython.data import Attr, Class, Module
1.18 from micropython.errors import *
1.19
1.20 @@ -35,6 +36,23 @@
1.21
1.22 "A base class for visitors."
1.23
1.24 + def process_definitions(self, node):
1.25 +
1.26 + """
1.27 + Process and return all definitions beneath 'node', but not definitions
1.28 + within definitions.
1.29 + """
1.30 +
1.31 + definitions = []
1.32 + for n in node.getChildNodes():
1.33 + if isinstance(n, (compiler.ast.Class, compiler.ast.Function)):
1.34 + definitions.append(self.dispatch(n))
1.35 + else:
1.36 + definitions += self.process_definitions(n)
1.37 + return definitions
1.38 +
1.39 + # Visitor support methods.
1.40 +
1.41 def default(self, node, *args):
1.42 for n in node.getChildNodes():
1.43 self.dispatch(n)
1.44 @@ -56,6 +74,25 @@
1.45 exc.unit_name = self.get_unit().full_name()
1.46 raise
1.47
1.48 + # Deduction-related methods.
1.49 +
1.50 + def provides_self_access(self, node, unit):
1.51 +
1.52 + """
1.53 + Return whether the 'node' in the given 'unit' provides a self-based
1.54 + attribute access.
1.55 + """
1.56 +
1.57 + attr_value = self.get_attribute_and_value(node._expr)
1.58 +
1.59 + if attr_value:
1.60 + target, value = attr_value
1.61 +
1.62 + return target and target.name == "self" and target.parent is unit and \
1.63 + unit.is_method()
1.64 +
1.65 + return False
1.66 +
1.67 def possible_accessor_types(self, node, defining_users=1):
1.68
1.69 """
1.70 @@ -74,69 +111,22 @@
1.71 # not that of a general instance or an unresolved name, attempt to
1.72 # identify it.
1.73
1.74 - if isinstance(node, (AssAttr, Getattr, Name)):
1.75 -
1.76 - # Use any explicit attribute annotation.
1.77 -
1.78 - if isinstance(node._attr, Attr):
1.79 - attr = node._attr
1.80 - all_target_names.append(set([(attr.parent.full_name(), attr.is_static_attribute())]))
1.81 -
1.82 - # Otherwise, try and use an expression annotation.
1.83 -
1.84 - if isinstance(node, (AssAttr, Getattr)):
1.85 - expr = node._expr
1.86 -
1.87 - # Permitting multiple expression types if they provide the
1.88 - # attribute.
1.89 -
1.90 - if isinstance(expr, Attr):
1.91 - exprs = expr.get_values()
1.92 - elif expr:
1.93 - exprs = [expr]
1.94 - else:
1.95 - exprs = None
1.96 -
1.97 - if exprs:
1.98 - target_names = set()
1.99 -
1.100 - # For each expression value try and get a concrete
1.101 - # attribute.
1.102 -
1.103 - for expr in exprs:
1.104 - attr = expr.all_attributes().get(node.attrname)
1.105 + # Use explicit annotations on the node.
1.106
1.107 - # Where an attribute can be obtained, record its
1.108 - # details.
1.109 -
1.110 - if attr:
1.111 - target_names.add((attr.parent.full_name(), attr.is_static_attribute()))
1.112 -
1.113 - if target_names:
1.114 - all_target_names.append(target_names)
1.115 -
1.116 - # Otherwise, attempt to employ the attribute usage observations.
1.117 -
1.118 - if node._attrusers:
1.119 - target_names = set()
1.120 -
1.121 - # Visit each attribute user.
1.122 + attrs = self.possible_attributes_from_annotation(node)
1.123 + if attrs:
1.124 + target_names = set()
1.125 + for (attr, value) in attrs:
1.126 + # NOTE: Ignoring constant objects.
1.127 + if attr:
1.128 + target_names.add((attr.parent.full_name(), attr.is_static_attribute()))
1.129 + all_target_names.append(target_names)
1.130
1.131 - for user in node._attrusers:
1.132 -
1.133 - # Since users such as branches may not provide type information,
1.134 - # attempt to find defining users.
1.135 + # Use attribute usage observations.
1.136
1.137 - if defining_users:
1.138 - for def_user in user._attrdefs or [user]:
1.139 - for target_name, is_static in def_user._attrtypes.get(node._username, []):
1.140 - target_names.add((target_name, is_static))
1.141 - else:
1.142 - for target_name, is_static in user._attrspecifictypes.get(node._username, []):
1.143 - target_names.add((target_name, is_static))
1.144 -
1.145 - if target_names:
1.146 - all_target_names.append(target_names)
1.147 + target_names = self.possible_accessor_types_from_usage(node, defining_users)
1.148 + if target_names:
1.149 + all_target_names.append(target_names)
1.150
1.151 # Return the smallest set of target names.
1.152
1.153 @@ -144,6 +134,112 @@
1.154
1.155 return all_target_names and all_target_names[0]
1.156
1.157 + def get_attribute_and_value(self, obj):
1.158 +
1.159 + """
1.160 + Return (attribute, value) details for the given 'obj', where an
1.161 + attribute of None can be returned for constant objects, and where None
1.162 + can be returned as the result where no concrete details can be provided.
1.163 + """
1.164 +
1.165 + if isinstance(obj, Constant):
1.166 + return None, obj
1.167 +
1.168 + if isinstance(obj, Attr):
1.169 + return obj, obj.get_value()
1.170 +
1.171 + return None
1.172 +
1.173 + def possible_attributes_from_annotation(self, node):
1.174 +
1.175 + """
1.176 + Return (attribute, value) details provided by any _expr or _attr
1.177 + annotations on 'node'.
1.178 + """
1.179 +
1.180 + attr_value = self.get_attribute_and_value(node._attr)
1.181 +
1.182 + if attr_value:
1.183 + return [attr_value]
1.184 +
1.185 + attrs = set()
1.186 + expr = node._expr
1.187 +
1.188 + if expr:
1.189 +
1.190 + # Permitting multiple expression types if they provide the
1.191 + # attribute.
1.192 +
1.193 + if isinstance(expr, Attr):
1.194 + exprs = expr.get_values()
1.195 + else:
1.196 + exprs = [expr]
1.197 +
1.198 + # For each expression value try and get a concrete
1.199 + # attribute.
1.200 +
1.201 + for expr in exprs:
1.202 + attr = expr.all_attributes().get(node.attrname)
1.203 +
1.204 + # Where an attribute can be obtained, record its
1.205 + # details.
1.206 +
1.207 + if attr:
1.208 + attrs.add((attr, attr.get_value()))
1.209 +
1.210 + return attrs
1.211 +
1.212 + def possible_accessor_types_from_usage(self, node, defining_users=1):
1.213 +
1.214 + """
1.215 + Return a set of (target name, static) tuples from an investigation of
1.216 + attribute usage observations stored on the given 'node'.
1.217 +
1.218 + If 'defining_users' is set to a false value, attempt to get the type
1.219 + names specifically applicable to the node, rather than retrieving more
1.220 + general definition-based type observations.
1.221 + """
1.222 +
1.223 + target_names = set()
1.224 +
1.225 + if node._attrusers:
1.226 +
1.227 + # Visit each attribute user.
1.228 +
1.229 + for user in node._attrusers:
1.230 +
1.231 + # Since users such as branches may not provide type information,
1.232 + # attempt to find defining users.
1.233 +
1.234 + if defining_users:
1.235 + for def_user in user._attrdefs or [user]:
1.236 + for target_name, is_static in def_user._attrtypes.get(node._username, []):
1.237 + target_names.add((target_name, is_static))
1.238 + else:
1.239 + for target_name, is_static in user._attrspecifictypes.get(node._username, []):
1.240 + target_names.add((target_name, is_static))
1.241 +
1.242 + return target_names
1.243 +
1.244 + def possible_accessors_from_usage(self, node, defining_users=1):
1.245 +
1.246 + """
1.247 + Return possible accessors from the usage recorded on the given 'node'.
1.248 +
1.249 + If 'defining_users' is set to a false value, attempt to get the type
1.250 + names specifically applicable to the node, rather than retrieving more
1.251 + general definition-based type observations.
1.252 + """
1.253 +
1.254 + targets = set()
1.255 + target_names = self.possible_accessor_types_from_usage(node, defining_users)
1.256 +
1.257 + if target_names:
1.258 + for target_name, is_static in target_names:
1.259 + targets.add(self.objtable.get_object(target_name))
1.260 +
1.261 + return targets
1.262 +
1.263 def used_by_unit(node):
1.264
1.265 """