1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/micropython/deduce.py Thu Apr 25 18:08:32 2013 +0200
1.3 @@ -0,0 +1,242 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Perform deductions on an inspected program.
1.8 +
1.9 +Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Paul Boddie <paul@boddie.org.uk>
1.10 +
1.11 +This program is free software; you can redistribute it and/or modify it under
1.12 +the terms of the GNU General Public License as published by the Free Software
1.13 +Foundation; either version 3 of the License, or (at your option) any later
1.14 +version.
1.15 +
1.16 +This program is distributed in the hope that it will be useful, but WITHOUT
1.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.19 +details.
1.20 +
1.21 +You should have received a copy of the GNU General Public License along with
1.22 +this program. If not, see <http://www.gnu.org/licenses/>.
1.23 +"""
1.24 +
1.25 +from micropython.common import *
1.26 +from micropython.data import *
1.27 +from micropython.errors import *
1.28 +import compiler.ast
1.29 +
1.30 +# Source code classes.
1.31 +
1.32 +class DeducedSource(ASTVisitor):
1.33 +
1.34 + "A module upon which deductions of code behaviour are made."
1.35 +
1.36 + def __init__(self, module, program):
1.37 + self.visitor = self
1.38 + self.module = module
1.39 + self.program = program
1.40 + self.objtable = program.get_object_table()
1.41 + self.units = []
1.42 +
1.43 + def get_unit(self):
1.44 + return self.units[-1]
1.45 +
1.46 + def get_module(self):
1.47 + return self.units[0]
1.48 +
1.49 + def deduce(self):
1.50 +
1.51 + "Process the module, making deductions."
1.52 +
1.53 + self.dispatch(self.module.astnode)
1.54 +
1.55 + def dispatch(self, node, *args):
1.56 +
1.57 + "NOTE: From compiler.visitor.ASTVisitor."
1.58 +
1.59 + try:
1.60 + return node.visit(self.visitor, *args)
1.61 + except AttributeError:
1.62 + # NOTE: Obligatory hack to find real attribute errors.
1.63 + if isinstance(node, (Getattr, AssAttr)):
1.64 + raise
1.65 + return self.visitor.default(node, *args)
1.66 +
1.67 + def _visitUnit(self, node):
1.68 +
1.69 + """
1.70 + Track entry into program units in order to support various attribute
1.71 + access operations.
1.72 + """
1.73 +
1.74 + if not used_by_unit(node):
1.75 + return
1.76 +
1.77 + self.units.append(node.unit)
1.78 + self.dispatch(node.node)
1.79 + self.units.pop()
1.80 +
1.81 + visitModule = visitClass = visitFunction = _visitUnit
1.82 +
1.83 + def _visitAttr(self, node):
1.84 +
1.85 + """
1.86 + Perform deductions on attribute accesses, adding annotations to the node
1.87 + that can be used by subsequent activities.
1.88 + """
1.89 +
1.90 + unit = self.get_unit()
1.91 +
1.92 + # The target, on which the access is performed, may influence the effect
1.93 + # on the context. We can only reliably assume that a literal constant is
1.94 + # an instance: all other "instances" may actually be classes in certain
1.95 + # cases.
1.96 +
1.97 + target = node._expr
1.98 + instance_target = isinstance(target, Const)
1.99 +
1.100 + # Attempt to deduce attributes from explicit annotations.
1.101 +
1.102 + node._attrs_deduced = attrs = self.possible_attributes_from_annotation(node)
1.103 +
1.104 + if len(attrs) == 1:
1.105 + for attr, value in attrs:
1.106 +
1.107 + # Constant values can be obtained directly.
1.108 +
1.109 + if self.provides_constant_result(value):
1.110 + node._access_type = "constant"
1.111 + node._value_deduced = value
1.112 + return
1.113 +
1.114 + # Static attributes can be obtained via their parent.
1.115 +
1.116 + if attr.is_static_attribute():
1.117 + node._access_type = "static"
1.118 + node._attr_deduced = attr
1.119 + node._set_context = instance_target and "set" or None
1.120 + return
1.121 +
1.122 + # Attributes of self, which is by definition an instance.
1.123 +
1.124 + if self.provides_self_access(node, unit):
1.125 +
1.126 + # Find instance attributes.
1.127 +
1.128 + attr = unit.parent.instance_attributes().get(node.attrname)
1.129 +
1.130 + if attr:
1.131 + node._access_type = "instance"
1.132 + node._attr_deduced = attr
1.133 + return
1.134 +
1.135 + # Find class attributes.
1.136 + # The context will be overridden for compatible class attributes
1.137 + # only.
1.138 +
1.139 + attr = unit.parent.get(node.attrname)
1.140 +
1.141 + if attr:
1.142 +
1.143 + # Constant attributes.
1.144 +
1.145 + if attr.is_strict_constant():
1.146 + if self.provides_constant_result(attr.get_value()):
1.147 + node._access_type = "constant"
1.148 + node._value_deduced = attr.get_value()
1.149 + return
1.150 +
1.151 + # Compatible class attributes.
1.152 +
1.153 + if attr.defined_within_hierarchy():
1.154 + node._access_type = "static"
1.155 + node._attr_deduced = attr
1.156 + node._set_context = "set"
1.157 + return
1.158 +
1.159 + # Incompatible class attributes.
1.160 +
1.161 + elif attr.defined_outside_hierarchy():
1.162 + node._access_type = "static"
1.163 + node._attr_deduced = attr
1.164 + return
1.165 +
1.166 + # Unknown or mixed compatibility.
1.167 +
1.168 + node._access_type = "static"
1.169 + node._attr_deduced = attr
1.170 + node._set_context = "cond"
1.171 + return
1.172 +
1.173 + # Usage observations, both specific to this node's region of the program
1.174 + # and also applicable to the lifespan of the affected name.
1.175 +
1.176 + specific_targets = self.possible_accessors_from_usage(node, defining_users=0)
1.177 + targets = self.possible_accessors_from_usage(node, defining_users=1)
1.178 +
1.179 + # Record whether types were already deduced. If not, get types using
1.180 + # only this attribute.
1.181 +
1.182 + if not specific_targets or not targets:
1.183 + attribute_targets = self.possible_accessors_for_attribute(node.attrname)
1.184 + if not specific_targets:
1.185 + specific_targets = attribute_targets
1.186 + if not targets:
1.187 + targets = attribute_targets
1.188 +
1.189 + node._attrs_deduced_from_specific_usage = self.get_attributes(specific_targets, node.attrname)
1.190 + node._attrs_deduced_from_usage = attrs = self.get_attributes(targets, node.attrname)
1.191 +
1.192 + # Generate optimisations where only a single attribute applies.
1.193 +
1.194 + if attrs and len(attrs) == 1:
1.195 + for attr in attrs:
1.196 +
1.197 + # Static attributes, but potentially non-static targets.
1.198 +
1.199 + if attr.is_static_attribute():
1.200 +
1.201 + # Static attributes may be accompanied by a different context
1.202 + # depending on the accessor.
1.203 + # NOTE: Should determine whether the context is always replaced.
1.204 +
1.205 + node._access_type = "static"
1.206 + node._attr_deduced = attr
1.207 + node._set_context = instance_target and "set" or "cond"
1.208 + return
1.209 +
1.210 + # Non-static attributes.
1.211 +
1.212 + node._access_type = "instance"
1.213 + node._attr_deduced = attr
1.214 + return
1.215 +
1.216 + # Test for compatible attribute positioning.
1.217 +
1.218 + elif attrs:
1.219 + positions = set([(attr.is_static_attribute(), attr.position) for attr in attrs])
1.220 +
1.221 + # Permit a position-based access only on non-static attributes since
1.222 + # access to static attributes may happen via instances and thus not
1.223 + # be relative to the accessor but to its parent.
1.224 +
1.225 + if len(positions) == 1:
1.226 + for position in positions:
1.227 + if not position[0]:
1.228 + node._access_type = "positioned"
1.229 + node._position_deduced = position[0]
1.230 + return
1.231 +
1.232 + # With no usable deductions, generate a table-based access.
1.233 +
1.234 + node._access_type = "unknown"
1.235 + node._set_context = "cond"
1.236 +
1.237 + visitAssAttr = visitGetattr = _visitAttr
1.238 +
1.239 +# Convenience functions.
1.240 +
1.241 +def deduce(program):
1.242 + for module in program.get_importer().get_modules():
1.243 + DeducedSource(module, program).deduce()
1.244 +
1.245 +# vim: tabstop=4 expandtab shiftwidth=4