1.1 --- a/deducer.py Fri Mar 03 21:35:02 2017 +0100
1.2 +++ b/deducer.py Sat Mar 04 00:20:25 2017 +0100
1.3 @@ -23,8 +23,8 @@
1.4 get_attrname_from_location, get_attrnames, \
1.5 get_invoked_attributes, get_name_path, init_item, \
1.6 sorted_output, CommonOutput
1.7 -from encoders import encode_access_location, \
1.8 - encode_constrained, encode_location, encode_usage, \
1.9 +from encoders import encode_access_location, encode_constrained, \
1.10 + encode_instruction, encode_location, encode_usage, \
1.11 get_kinds, test_label_for_kind, test_label_for_type
1.12 from errors import DeduceError
1.13 from os.path import join
1.14 @@ -110,6 +110,11 @@
1.15
1.16 self.access_plans = {}
1.17
1.18 + # Specific attribute access information.
1.19 +
1.20 + self.access_instructions = {}
1.21 + self.accessor_kinds = {}
1.22 +
1.23 # Accumulated information about accessors and providers.
1.24
1.25 self.accessor_general_class_types = {}
1.26 @@ -143,6 +148,7 @@
1.27 self.classify_accessors()
1.28 self.classify_accesses()
1.29 self.initialise_access_plans()
1.30 + self.initialise_access_instructions()
1.31 self.identify_dependencies()
1.32
1.33 def to_output(self):
1.34 @@ -407,11 +413,21 @@
1.35 def write_access_plans(self):
1.36
1.37 """
1.38 + Write access and instruction plans.
1.39 +
1.40 Each attribute access is written out as a plan of the following form:
1.41
1.42 location " " name " " test " " test type " " base " " traversed attributes
1.43 - " " attributes to traverse " " context " " access method
1.44 - " " static attribute " " accessor kinds
1.45 + " " traversal access modes " " attributes to traverse
1.46 + " " context " " context test " " first access method
1.47 + " " final access method " " static attribute " " accessor kinds
1.48 +
1.49 + Locations have the following format:
1.50 +
1.51 + qualified name of scope "." local name ":" name version
1.52 +
1.53 + Traversal access modes are either "class" (obtain accessor class to
1.54 + access attribute) or "object" (obtain attribute directly from accessor).
1.55 """
1.56
1.57 f_attrs = open(join(self.output, "attribute_plans"), "w")
1.58 @@ -442,6 +458,20 @@
1.59 finally:
1.60 f_attrs.close()
1.61
1.62 + f = open(join(self.output, "instruction_plans"), "w")
1.63 + try:
1.64 + access_instructions = self.access_instructions.items()
1.65 + access_instructions.sort()
1.66 +
1.67 + for location, instructions in access_instructions:
1.68 + print >>f, encode_access_location(location), "..."
1.69 + for instruction in instructions:
1.70 + print >>f, encode_instruction(instruction)
1.71 + print >>f
1.72 +
1.73 + finally:
1.74 + f.close()
1.75 +
1.76 def classify_accessors(self):
1.77
1.78 "For each program location, classify accessors."
1.79 @@ -1919,6 +1949,7 @@
1.80 * remaining attributes needing to be tested and traversed
1.81 * details of the context
1.82 * any test to apply to the context
1.83 + * the method of obtaining the first attribute
1.84 * the method of obtaining the final attribute
1.85 * any static final attribute
1.86 * the kinds of objects providing the final attribute
1.87 @@ -2223,4 +2254,294 @@
1.88 first_method, final_method, \
1.89 origin, accessor_kinds
1.90
1.91 + def initialise_access_instructions(self):
1.92 +
1.93 + "Expand access plans into instruction sequences."
1.94 +
1.95 + for access_location, access_plan in self.access_plans.items():
1.96 +
1.97 + # Obtain the access details.
1.98 +
1.99 + name, test, test_type, base, \
1.100 + traversed, traversal_modes, attrnames, \
1.101 + context, context_test, \
1.102 + first_method, final_method, \
1.103 + origin, accessor_kinds = access_plan
1.104 +
1.105 + # Emit instructions by appending them to a list.
1.106 +
1.107 + instructions = []
1.108 + emit = instructions.append
1.109 +
1.110 + # Identify any static original accessor.
1.111 +
1.112 + if base:
1.113 + original_accessor = base
1.114 +
1.115 + # Employ names as contexts unless the context needs testing and
1.116 + # potentially updating. In such cases, temporary context storage is
1.117 + # used instead.
1.118 +
1.119 + elif name and not (context_test == "test" and
1.120 + final_method in ("access-invoke", "static-invoke")):
1.121 + original_accessor = "<name>" # refers to the name
1.122 +
1.123 + # Use a generic placeholder representing the access expression in
1.124 + # the general case.
1.125 +
1.126 + else:
1.127 + original_accessor = "<expr>"
1.128 +
1.129 + # Prepare for any first attribute access.
1.130 +
1.131 + if traversed:
1.132 + attrname = traversed[0]
1.133 + del traversed[0]
1.134 + elif attrnames:
1.135 + attrname = attrnames[0]
1.136 + del attrnames[0]
1.137 +
1.138 + # Perform the first access explicitly if at least one operation
1.139 + # requires it.
1.140 +
1.141 + access_first_attribute = final_method in ("access", "access-invoke", "assign") or traversed or attrnames
1.142 +
1.143 + # Determine whether the first access involves assignment.
1.144 +
1.145 + assigning = not traversed and not attrnames and final_method == "assign"
1.146 + set_accessor = assigning and "<set_target_accessor>" or "<set_accessor>"
1.147 + stored_accessor = assigning and "<target_accessor>" or "<accessor>"
1.148 +
1.149 + # Set the context if already available.
1.150 +
1.151 + context_var = None
1.152 +
1.153 + if context == "base":
1.154 + accessor = context_var = (base,)
1.155 + elif context == "original-accessor":
1.156 +
1.157 + # Prevent re-evaluation of any dynamic expression by storing it.
1.158 +
1.159 + if original_accessor == "<expr>":
1.160 + if final_method in ("access-invoke", "static-invoke"):
1.161 + emit(("<set_context>", original_accessor))
1.162 + accessor = context_var = ("<context>",)
1.163 + else:
1.164 + emit((set_accessor, original_accessor))
1.165 + accessor = context_var = (stored_accessor,)
1.166 + else:
1.167 + accessor = context_var = (original_accessor,)
1.168 +
1.169 + # Assigning does not set the context.
1.170 +
1.171 + elif context in ("final-accessor", "unset") and access_first_attribute:
1.172 +
1.173 + # Prevent re-evaluation of any dynamic expression by storing it.
1.174 +
1.175 + if original_accessor == "<expr>":
1.176 + emit((set_accessor, original_accessor))
1.177 + accessor = (stored_accessor,)
1.178 + else:
1.179 + accessor = (original_accessor,)
1.180 +
1.181 + # Apply any test.
1.182 +
1.183 + if test[0] == "test":
1.184 + accessor = ("__%s_%s_%s" % test, accessor, test_type)
1.185 +
1.186 + # Perform the first or final access.
1.187 + # The access only needs performing if the resulting accessor is used.
1.188 +
1.189 + remaining = len(traversed + attrnames)
1.190 +
1.191 + if access_first_attribute:
1.192 +
1.193 + if first_method == "relative-class":
1.194 + if assigning:
1.195 + emit(("__store_via_class", accessor, attrname, "<assexpr>"))
1.196 + else:
1.197 + accessor = ("__load_via_class", accessor, attrname)
1.198 +
1.199 + elif first_method == "relative-object":
1.200 + if assigning:
1.201 + emit(("__store_via_object", accessor, attrname, "<assexpr>"))
1.202 + else:
1.203 + accessor = ("__load_via_object", accessor, attrname)
1.204 +
1.205 + elif first_method == "relative-object-class":
1.206 + if assigning:
1.207 + emit(("__get_class_and_store", accessor, attrname, "<assexpr>"))
1.208 + else:
1.209 + accessor = ("__get_class_and_load", accessor, attrname)
1.210 +
1.211 + elif first_method == "check-class":
1.212 + if assigning:
1.213 + emit(("__check_and_store_via_class", accessor, attrname, "<assexpr>"))
1.214 + else:
1.215 + accessor = ("__check_and_load_via_class", accessor, attrname)
1.216 +
1.217 + elif first_method == "check-object":
1.218 + if assigning:
1.219 + emit(("__check_and_store_via_object", accessor, attrname, "<assexpr>"))
1.220 + else:
1.221 + accessor = ("__check_and_load_via_object", accessor, attrname)
1.222 +
1.223 + elif first_method == "check-object-class":
1.224 + if assigning:
1.225 + emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>"))
1.226 + else:
1.227 + accessor = ("__check_and_load_via_any", accessor, attrname)
1.228 +
1.229 + # Traverse attributes using the accessor.
1.230 +
1.231 + if traversed:
1.232 + for attrname, traversal_mode in zip(traversed, traversal_modes):
1.233 + assigning = remaining == 1 and final_method == "assign"
1.234 +
1.235 + # Set the context, if appropriate.
1.236 +
1.237 + if remaining == 1 and final_method != "assign" and context == "final-accessor":
1.238 +
1.239 + # Invoked attributes employ a separate context accessed
1.240 + # during invocation.
1.241 +
1.242 + if final_method in ("access-invoke", "static-invoke"):
1.243 + emit(("<set_context>", accessor))
1.244 + accessor = context_var = "<context>"
1.245 +
1.246 + # A private context within the access is otherwise
1.247 + # retained.
1.248 +
1.249 + else:
1.250 + emit(("<set_private_context>", accessor))
1.251 + accessor = context_var = "<private_context>"
1.252 +
1.253 + # Perform the access only if not achieved directly.
1.254 +
1.255 + if remaining > 1 or final_method in ("access", "access-invoke", "assign"):
1.256 +
1.257 + if traversal_mode == "class":
1.258 + if assigning:
1.259 + emit(("__store_via_class", accessor, attrname, "<assexpr>"))
1.260 + else:
1.261 + accessor = ("__load_via_class", accessor, attrname)
1.262 + else:
1.263 + if assigning:
1.264 + emit(("__store_via_object", accessor, attrname, "<assexpr>"))
1.265 + else:
1.266 + accessor = ("__load_via_object", accessor, attrname)
1.267 +
1.268 + remaining -= 1
1.269 +
1.270 + if attrnames:
1.271 + for attrname in attrnames:
1.272 + assigning = remaining == 1 and final_method == "assign"
1.273 +
1.274 + # Set the context, if appropriate.
1.275 +
1.276 + if remaining == 1 and final_method != "assign" and context == "final-accessor":
1.277 +
1.278 + # Invoked attributes employ a separate context accessed
1.279 + # during invocation.
1.280 +
1.281 + if final_method in ("access-invoke", "static-invoke"):
1.282 + emit(("<set_context>", accessor))
1.283 + accessor = context_var = "<context>"
1.284 +
1.285 + # A private context within the access is otherwise
1.286 + # retained.
1.287 +
1.288 + else:
1.289 + emit(("<set_private_context>", accessor))
1.290 + accessor = context_var = "<private_context>"
1.291 +
1.292 + # Perform the access only if not achieved directly.
1.293 +
1.294 + if remaining > 1 or final_method in ("access", "access-invoke", "assign"):
1.295 +
1.296 + if assigning:
1.297 + emit(("__check_and_store_via_any", accessor, attrname, "<assexpr>"))
1.298 + else:
1.299 + accessor = ("__check_and_load_via_any", accessor, attrname)
1.300 +
1.301 + remaining -= 1
1.302 +
1.303 + # Define or emit the means of accessing the actual target.
1.304 +
1.305 + # Assignments to known attributes.
1.306 +
1.307 + if final_method == "static-assign":
1.308 + parent, attrname = origin.rsplit(".", 1)
1.309 + emit(("__store_via_object", parent, attrname, "<assexpr>"))
1.310 +
1.311 + # Invoked attributes employ a separate context.
1.312 +
1.313 + elif final_method in ("static", "static-invoke"):
1.314 + accessor = ("__load_static_ignore", origin)
1.315 +
1.316 + # Wrap accesses in context operations.
1.317 +
1.318 + if context_test == "test":
1.319 +
1.320 + # Test and combine the context with static attribute details.
1.321 +
1.322 + if final_method == "static":
1.323 + emit(("__load_static_test", context_var, origin))
1.324 +
1.325 + # Test the context, storing it separately if required for the
1.326 + # immediately invoked static attribute.
1.327 +
1.328 + elif final_method == "static-invoke":
1.329 + emit(("<test_context_static>", context_var, origin))
1.330 +
1.331 + # Test the context, storing it separately if required for an
1.332 + # immediately invoked attribute.
1.333 +
1.334 + elif final_method == "access-invoke":
1.335 + emit(("<test_context_revert>", context_var, accessor))
1.336 +
1.337 + # Test the context and update the attribute details if
1.338 + # appropriate.
1.339 +
1.340 + else:
1.341 + emit(("__test_context", context_var, accessor))
1.342 +
1.343 + elif context_test == "replace":
1.344 +
1.345 + # Produce an object with updated context.
1.346 +
1.347 + if final_method == "static":
1.348 + emit(("__load_static_replace", context_var, origin))
1.349 +
1.350 + # Omit the context update operation where the target is static
1.351 + # and the context is recorded separately.
1.352 +
1.353 + elif final_method == "static-invoke":
1.354 + pass
1.355 +
1.356 + # If a separate context is used for an immediate invocation,
1.357 + # produce the attribute details unchanged.
1.358 +
1.359 + elif final_method == "access-invoke":
1.360 + emit(accessor)
1.361 +
1.362 + # Update the context in the attribute details.
1.363 +
1.364 + else:
1.365 + emit(("__update_context", context_var, accessor))
1.366 +
1.367 + # Omit the accessor for assignments and for invocations of static
1.368 + # targets.
1.369 +
1.370 + elif final_method not in ("assign", "static-assign", "static-invoke"):
1.371 + emit(accessor)
1.372 +
1.373 + # Produce an advisory instruction regarding the context.
1.374 +
1.375 + if context_var:
1.376 + emit(("<context_identity>", context_var))
1.377 +
1.378 + self.access_instructions[access_location] = instructions
1.379 + self.accessor_kinds[access_location] = accessor_kinds
1.380 +
1.381 # vim: tabstop=4 expandtab shiftwidth=4