2.1 --- a/translator.py Sat Jun 23 18:01:56 2018 +0200
2.2 +++ b/translator.py Sun Jun 24 00:09:33 2018 +0200
2.3 @@ -3,7 +3,7 @@
2.4 """
2.5 Translate programs.
2.6
2.7 -Copyright (C) 2015, 2016, 2017 Paul Boddie <paul@boddie.org.uk>
2.8 +Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
2.9
2.10 This program is free software; you can redistribute it and/or modify it under
2.11 the terms of the GNU General Public License as published by the Free Software
2.12 @@ -25,7 +25,8 @@
2.13 from encoders import encode_access_instruction, encode_access_instruction_arg, \
2.14 encode_function_pointer, encode_literal_instantiator, \
2.15 encode_instantiator_pointer, encode_path, encode_symbol, \
2.16 - encode_type_attribute, is_type_attribute
2.17 + encode_type_attribute, is_type_attribute, \
2.18 + type_ops, typename_ops
2.19 from errors import InspectError, TranslateError
2.20 from os.path import exists, join
2.21 from os import makedirs
2.22 @@ -120,10 +121,6 @@
2.23 self.in_try_finally = False
2.24 self.in_try_except = False
2.25
2.26 - # Invocation adjustments.
2.27 -
2.28 - self.in_argument_list = False
2.29 -
2.30 # Attribute access and accessor counting.
2.31
2.32 self.attr_accesses = {}
2.33 @@ -317,7 +314,9 @@
2.34 else:
2.35 self.in_function = False
2.36 self.function_target = 0
2.37 - self.max_function_targets = 0
2.38 + self.max_function_target = 0
2.39 + self.context_index = 0
2.40 + self.max_context_index = 0
2.41 self.start_module()
2.42 self.process_structure(node)
2.43 self.end_module()
2.44 @@ -555,10 +554,11 @@
2.45 # The context set or retrieved will be that used by any enclosing
2.46 # invocation.
2.47
2.48 - context_index = self.function_target - 1
2.49 + context_index = self.context_index
2.50 context_identity = None
2.51 context_identity_verified = False
2.52 final_identity = None
2.53 + accessor_test = False
2.54
2.55 # Obtain encoded versions of each instruction, accumulating temporary
2.56 # variables.
2.57 @@ -579,6 +579,12 @@
2.58 final_identity = instruction[1]
2.59 continue
2.60
2.61 + # Modify test instructions.
2.62 +
2.63 + elif instruction[0] in typename_ops or instruction[0] in type_ops:
2.64 + instruction = ("__to_error", instruction)
2.65 + accessor_test = True
2.66 +
2.67 # Collect the encoded instruction, noting any temporary variables
2.68 # required by it.
2.69
2.70 @@ -599,7 +605,7 @@
2.71 refs = [ref]
2.72
2.73 del self.attrs[0]
2.74 - return AttrResult(output, refs, location, context_identity, context_identity_verified)
2.75 + return AttrResult(output, refs, location, context_identity, context_identity_verified, accessor_test)
2.76
2.77 def init_substitutions(self):
2.78
2.79 @@ -837,7 +843,9 @@
2.80 in_conditional = self.in_conditional
2.81 self.in_conditional = False
2.82 self.function_target = 0
2.83 - self.max_function_targets = 0
2.84 + self.max_function_target = 0
2.85 + self.context_index = 0
2.86 + self.max_context_index = 0
2.87
2.88 # Volatile locals for exception handling.
2.89
2.90 @@ -1040,23 +1048,10 @@
2.91
2.92 "Process the given invocation node 'n'."
2.93
2.94 - # Any invocations in the expression will store target details in a
2.95 - # different location.
2.96 -
2.97 - self.next_target()
2.98 -
2.99 - in_argument_list = self.in_argument_list
2.100 - self.in_argument_list = False
2.101 -
2.102 # Process the expression.
2.103
2.104 expr = self.process_structure_node(n.node)
2.105
2.106 - # Reference the current target again.
2.107 -
2.108 - self.in_argument_list = in_argument_list
2.109 - self.function_target -= 1
2.110 -
2.111 # Obtain details of the invocation expression.
2.112
2.113 objpath = expr.get_origin()
2.114 @@ -1083,6 +1078,7 @@
2.115 have_access_context = isinstance(expr, AttrResult)
2.116 context_identity = have_access_context and expr.context()
2.117 context_verified = have_access_context and expr.context_verified()
2.118 + tests_accessor = have_access_context and expr.tests_accessor()
2.119 parameters = None
2.120 num_parameters = None
2.121 num_defaults = None
2.122 @@ -1204,28 +1200,61 @@
2.123 len(self.importer.function_parameters[_objpath]),
2.124 _objpath)
2.125
2.126 + # Logical statement about available parameter information.
2.127 +
2.128 + known_parameters = num_parameters is not None
2.129 +
2.130 + # The source of context information: target or temporary.
2.131 +
2.132 + need_context_target = context_required and not have_access_context
2.133 +
2.134 + need_context_stored = context_required and context_identity and \
2.135 + context_identity.startswith("__get_context")
2.136 +
2.137 # Determine any readily-accessible target identity.
2.138
2.139 target_named = expr.is_name() and str(expr) or None
2.140 + target_identity = target or target_named
2.141 +
2.142 + # Use of target information to populate defaults.
2.143 +
2.144 + defaults_target_var = not (parameters and function_defaults is not None) and \
2.145 + known_parameters and len(n.args) < num_parameters
2.146 +
2.147 + # Use of a temporary target variable in these situations:
2.148 + #
2.149 + # A target provided by an expression needed for defaults.
2.150 + #
2.151 + # A target providing the context but not using a name to do so.
2.152 + #
2.153 + # A target expression involving the definition of a context which may
2.154 + # then be evaluated and stored to ensure that the context is available
2.155 + # during argument evaluation.
2.156 + #
2.157 + # An expression featuring an accessor test.
2.158 +
2.159 + need_target_stored = defaults_target_var and not target_identity or \
2.160 + need_context_target and not target_named or \
2.161 + need_context_stored or \
2.162 + tests_accessor and not target
2.163 +
2.164 + # Define stored target details.
2.165 +
2.166 target_stored = "__tmp_targets[%d]" % self.function_target
2.167
2.168 - target_identity = target or target_named
2.169 - target_var = target_identity or target_stored
2.170 - context_var = target_named or target_stored
2.171 -
2.172 - if not target_identity:
2.173 + target_var = need_target_stored and target_stored or target_identity
2.174 + context_var = need_target_stored and target_stored or target_named
2.175 +
2.176 + if need_target_stored:
2.177 self.record_temp("__tmp_targets")
2.178
2.179 - if context_identity:
2.180 - if context_identity.startswith("__tmp_contexts"):
2.181 - self.record_temp("__tmp_contexts")
2.182 + if need_context_stored:
2.183 + self.record_temp("__tmp_contexts")
2.184
2.185 # Arguments are presented in a temporary frame array with any context
2.186 # always being the first argument. Where it would be unused, it may be
2.187 # set to null.
2.188
2.189 - known_parameters = num_parameters is not None
2.190 -
2.191 if context_required:
2.192 if have_access_context:
2.193 args = [context_identity]
2.194 @@ -1245,12 +1274,13 @@
2.195 # different location.
2.196
2.197 function_target = self.function_target
2.198 -
2.199 - if not target_identity:
2.200 + context_index = self.context_index
2.201 +
2.202 + if need_target_stored:
2.203 self.next_target()
2.204
2.205 - in_argument_list = self.in_argument_list
2.206 - self.in_argument_list = True
2.207 + if need_context_stored:
2.208 + self.next_context()
2.209
2.210 for i, arg in enumerate(n.args):
2.211 argexpr = self.process_structure_node(arg)
2.212 @@ -1290,10 +1320,8 @@
2.213
2.214 # Reference the current target again.
2.215
2.216 - self.in_argument_list = in_argument_list
2.217 -
2.218 - if not self.in_argument_list:
2.219 - self.function_target = function_target
2.220 + self.function_target = function_target
2.221 + self.context_index = context_index
2.222
2.223 # Defaults are added to the frame where arguments are missing.
2.224
2.225 @@ -1343,27 +1371,31 @@
2.226 # First, the invocation expression is presented.
2.227
2.228 stages = []
2.229 -
2.230 - # Without a known specific callable, the expression provides the target.
2.231 -
2.232 - if not target or context_required:
2.233 -
2.234 - # The context is set in the expression.
2.235 -
2.236 - if target and not target_named:
2.237 -
2.238 - # Test whether the expression provides anything.
2.239 -
2.240 - if expr:
2.241 - stages.append(str(expr))
2.242 -
2.243 - elif not target_identity:
2.244 - stages.append("%s = %s" % (target_var, expr))
2.245 + emit = stages.append
2.246 +
2.247 + # Assign and yield any stored target.
2.248 + # The context may be set in the expression.
2.249 +
2.250 + if need_target_stored:
2.251 + emit("%s = %s" % (target_var, expr))
2.252 + target_expr = target_var
2.253 +
2.254 + # Otherwise, retain the expression for later use.
2.255 +
2.256 + else:
2.257 + target_expr = str(expr)
2.258
2.259 # Any specific callable is then obtained for invocation.
2.260
2.261 if target:
2.262 - stages.append(target)
2.263 +
2.264 + # An expression involving a test of the accessor providing the target.
2.265 + # This must be emitted in order to perform the test.
2.266 +
2.267 + if tests_accessor:
2.268 + emit(str(expr))
2.269 +
2.270 + emit(target)
2.271
2.272 # Methods accessed via unidentified accessors are obtained for
2.273 # invocation.
2.274 @@ -1372,15 +1404,15 @@
2.275 if context_required:
2.276 if have_access_context:
2.277 if context_verified:
2.278 - stages.append("__get_function_member(%s)" % target_var)
2.279 + emit("__get_function_member(%s)" % target_expr)
2.280 else:
2.281 - stages.append("__get_function(%s, %s)" % (
2.282 - context_identity, target_var))
2.283 + emit("__get_function(%s, %s)" % (
2.284 + context_identity, target_expr))
2.285 else:
2.286 - stages.append("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % (
2.287 - context_var, target_var))
2.288 + emit("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % (
2.289 + context_var, target_expr))
2.290 else:
2.291 - stages.append("_get_function_member(%s)" % target_var)
2.292 + emit("_get_function_member(%s)" % target_expr)
2.293
2.294 # With known parameters, the target can be tested.
2.295
2.296 @@ -1388,11 +1420,11 @@
2.297 context_arg = context_required and args[0] or "__NULL"
2.298 if self.always_callable(refs):
2.299 if context_verified:
2.300 - stages.append("__get_function_member(%s)" % target_var)
2.301 + emit("__get_function_member(%s)" % target_expr)
2.302 else:
2.303 - stages.append("__get_function(%s, %s)" % (context_arg, target_var))
2.304 + emit("__get_function(%s, %s)" % (context_arg, target_expr))
2.305 else:
2.306 - stages.append("__check_and_get_function(%s, %s)" % (context_arg, target_var))
2.307 + emit("__check_and_get_function(%s, %s)" % (context_arg, target_expr))
2.308
2.309 # With a known target, the function is obtained directly and called.
2.310 # By putting the invocation at the end of the final element in the
2.311 @@ -1411,8 +1443,8 @@
2.312 # the callable and argument collections.
2.313
2.314 else:
2.315 - stages.append("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % (
2.316 - target_var,
2.317 + emit("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % (
2.318 + target_expr,
2.319 self.always_callable(refs) and 1 or 0,
2.320 len(kwargs), kwcodestr, kwargstr,
2.321 len(args), "__ARGS(%s)" % argstr))
2.322 @@ -1423,7 +1455,14 @@
2.323 "Allocate the next function target storage."
2.324
2.325 self.function_target += 1
2.326 - self.max_function_targets = max(self.function_target, self.max_function_targets)
2.327 + self.max_function_target = max(self.function_target, self.max_function_target)
2.328 +
2.329 + def next_context(self):
2.330 +
2.331 + "Allocate the next context value storage."
2.332 +
2.333 + self.context_index += 1
2.334 + self.max_context_index = max(self.context_index, self.max_context_index)
2.335
2.336 def always_callable(self, refs):
2.337
2.338 @@ -2061,12 +2100,15 @@
2.339
2.340 # Provide space for the given number of targets.
2.341
2.342 - targets = self.max_function_targets
2.343 + targets = self.max_function_target
2.344
2.345 if self.uses_temp(name, "__tmp_targets"):
2.346 self.writeline("__attr __tmp_targets[%d];" % targets)
2.347 +
2.348 + index = self.max_context_index
2.349 +
2.350 if self.uses_temp(name, "__tmp_contexts"):
2.351 - self.writeline("__attr __tmp_contexts[%d];" % targets)
2.352 + self.writeline("__attr __tmp_contexts[%d];" % index)
2.353
2.354 # Add temporary variable usage details.
2.355
3.1 --- a/transresults.py Sat Jun 23 18:01:56 2018 +0200
3.2 +++ b/transresults.py Sun Jun 24 00:09:33 2018 +0200
3.3 @@ -178,12 +178,14 @@
3.4
3.5 "A translation result for an attribute access."
3.6
3.7 - def __init__(self, instructions, refs, location, context_identity, context_identity_verified):
3.8 + def __init__(self, instructions, refs, location, context_identity,
3.9 + context_identity_verified, accessor_test):
3.10 InstructionSequence.__init__(self, instructions)
3.11 self.refs = refs
3.12 self.location = location
3.13 self.context_identity = context_identity
3.14 self.context_identity_verified = context_identity_verified
3.15 + self.accessor_test = accessor_test
3.16
3.17 def references(self):
3.18 return self.refs
3.19 @@ -197,6 +199,9 @@
3.20 def context_verified(self):
3.21 return self.context_identity_verified and self.context() or None
3.22
3.23 + def tests_accessor(self):
3.24 + return self.accessor_test
3.25 +
3.26 def get_origin(self):
3.27 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
3.28
3.29 @@ -215,7 +220,8 @@
3.30 return encode_instructions(self.instructions)
3.31
3.32 def __repr__(self):
3.33 - return "AttrResult(%r, %r, %r, %r)" % (self.instructions, self.refs, self.location, self.context_identity)
3.34 + return "AttrResult(%r, %r, %r, %r, %r)" % (self.instructions, self.refs,
3.35 + self.location, self.context_identity, self.accessor_test)
3.36
3.37 class AliasResult(NameRef, Result):
3.38