1.1 --- a/translator.py Sun Mar 19 00:56:43 2017 +0100
1.2 +++ b/translator.py Fri Mar 24 22:39:37 2017 +0100
1.3 @@ -29,7 +29,7 @@
1.4 from errors import InspectError, TranslateError
1.5 from os.path import exists, join
1.6 from os import makedirs
1.7 -from referencing import Reference
1.8 +from referencing import Reference, combine_types
1.9 from results import Result
1.10 from transresults import TrConstantValueRef, TrInstanceRef, \
1.11 TrLiteralSequenceRef, TrResolvedNameRef, \
1.12 @@ -542,7 +542,7 @@
1.13
1.14 subs = {
1.15 "<expr>" : attr_expr,
1.16 - "<name>" : "%s.value" % attr_expr,
1.17 + "<name>" : attr_expr,
1.18 "<assexpr>" : self.in_assignment,
1.19 }
1.20
1.21 @@ -557,6 +557,7 @@
1.22
1.23 context_index = self.function_target - 1
1.24 context_identity = None
1.25 + final_identity = None
1.26
1.27 # Obtain encoded versions of each instruction, accumulating temporary
1.28 # variables.
1.29 @@ -569,6 +570,13 @@
1.30 context_identity, _substituted = encode_access_instruction_arg(instruction[1], subs, instruction[0], context_index)
1.31 continue
1.32
1.33 + # Intercept a special instruction identifying the target. The value
1.34 + # is not encoded since it is used internally.
1.35 +
1.36 + if instruction[0] == "<final_identity>":
1.37 + final_identity = instruction[1]
1.38 + continue
1.39 +
1.40 # Collect the encoded instruction, noting any temporary variables
1.41 # required by it.
1.42
1.43 @@ -582,6 +590,12 @@
1.44 if self.temp_subs.has_key(sub):
1.45 self.record_temp(self.temp_subs[sub])
1.46
1.47 + # Get full final identity details.
1.48 +
1.49 + if final_identity and not refs:
1.50 + ref = self.importer.identify(final_identity)
1.51 + refs = [ref]
1.52 +
1.53 del self.attrs[0]
1.54 return AttrResult(output, refs, location, context_identity)
1.55
1.56 @@ -629,7 +643,18 @@
1.57 identified attributes.
1.58 """
1.59
1.60 + # Determine whether any deduced references refer to the accessed
1.61 + # attribute.
1.62 +
1.63 + path, accessor_name, attrnames, access_number = location
1.64 + attrnames = attrnames and attrnames.split(".")
1.65 + remaining = attrnames and len(attrnames) > 1
1.66 +
1.67 access_location = self.deducer.const_accesses.get(location)
1.68 +
1.69 + if remaining and not access_location:
1.70 + return []
1.71 +
1.72 refs = []
1.73 l = self.deducer.referenced_attrs.get(access_location or location)
1.74 if l:
1.75 @@ -862,14 +887,10 @@
1.76 else:
1.77 return
1.78
1.79 - # Produce an appropriate access to an attribute's value.
1.80 -
1.81 - name_to_value = "%s.value" % encode_path(name)
1.82 -
1.83 # Write a test that raises a TypeError upon failure.
1.84
1.85 - self.writestmt("if (!__test_%s_%s(%s, %s)) __raise_type_error();" % (
1.86 - guard, guard_type, name_to_value, argstr))
1.87 + self.writestmt("if (!__test_%s_%s(__VALUE(%s), %s)) __raise_type_error();" % (
1.88 + guard, guard_type, encode_path(name), argstr))
1.89
1.90 def process_function_node(self, n):
1.91
1.92 @@ -939,6 +960,8 @@
1.93
1.94 if not instance_name:
1.95 instance_name = "&%s" % encode_path(objpath)
1.96 + else:
1.97 + instance_name = "__VALUE(%s)" % instance_name
1.98
1.99 # Where defaults are involved but cannot be identified, obtain a new
1.100 # instance of the lambda and populate the defaults.
1.101 @@ -1021,6 +1044,7 @@
1.102
1.103 objpath = expr.get_origin()
1.104 location = expr.access_location()
1.105 + refs = expr.references()
1.106
1.107 # Identified target details.
1.108
1.109 @@ -1042,6 +1066,8 @@
1.110 have_access_context = isinstance(expr, AttrResult)
1.111 context_identity = have_access_context and expr.context()
1.112 parameters = None
1.113 + num_parameters = None
1.114 + num_defaults = None
1.115
1.116 # Obtain details of the callable and of its parameters.
1.117
1.118 @@ -1056,6 +1082,9 @@
1.119
1.120 elif objpath:
1.121 parameters = self.importer.function_parameters.get(objpath)
1.122 + function_defaults = self.importer.function_defaults.get(objpath)
1.123 + num_parameters = parameters and len(parameters) or 0
1.124 + num_defaults = function_defaults and len(function_defaults) or 0
1.125
1.126 # Class invocation involves instantiators.
1.127
1.128 @@ -1091,22 +1120,71 @@
1.129
1.130 target_structure = "&%s" % encode_path(objpath)
1.131
1.132 - # Other targets are retrieved at run-time. Some information about them
1.133 - # may be available and be used to provide warnings about argument
1.134 - # compatibility.
1.135 -
1.136 - elif self.importer.give_warning("args"):
1.137 - unsuitable = self.get_referenced_attribute_invocations(location)
1.138 -
1.139 - if unsuitable:
1.140 - for ref in unsuitable:
1.141 - _objpath = ref.get_origin()
1.142 - num_parameters = len(self.importer.function_parameters[_objpath])
1.143 - print >>sys.stderr, \
1.144 - "In %s, at line %d, inappropriate number of " \
1.145 - "arguments given. Need %d arguments to call %s." % (
1.146 - self.get_namespace_path(), n.lineno, num_parameters,
1.147 - _objpath)
1.148 + # Other targets are retrieved at run-time.
1.149 +
1.150 + else:
1.151 + if location:
1.152 + path, name, attrnames, access_number = location
1.153 + attrname = attrnames and attrnames.rsplit(".", 1)[-1]
1.154 +
1.155 + # Determine any common aspects of any attribute.
1.156 +
1.157 + if attrname:
1.158 + all_params = set()
1.159 + all_defaults = set()
1.160 + min_params = set()
1.161 + max_params = set()
1.162 + refs = set()
1.163 +
1.164 + # Obtain parameters and defaults for each possible target.
1.165 +
1.166 + for ref in self.get_attributes_for_attrname(attrname):
1.167 + origin = ref.get_origin()
1.168 + params = self.importer.function_parameters.get(origin)
1.169 +
1.170 + defaults = self.importer.function_defaults.get(origin)
1.171 + if defaults is not None:
1.172 + all_defaults.add(tuple(defaults))
1.173 +
1.174 + if params is not None:
1.175 + all_params.add(tuple(params))
1.176 + min_params.add(len(params) - (defaults and len(defaults) or 0))
1.177 + max_params.add(len(params))
1.178 + refs.add(ref)
1.179 + else:
1.180 + refs = set()
1.181 + break
1.182 +
1.183 + # Where the parameters and defaults are always the same,
1.184 + # permit populating them in advance.
1.185 +
1.186 + if refs:
1.187 + if self.uses_keyword_arguments(n):
1.188 + if len(all_params) == 1 and (not all_defaults or len(all_defaults) == 1):
1.189 + parameters = first(all_params)
1.190 + function_defaults = all_defaults and first(all_defaults) or []
1.191 + num_parameters = parameters and len(parameters) or 0
1.192 + num_defaults = function_defaults and len(function_defaults) or 0
1.193 + else:
1.194 + if len(min_params) == 1 and len(max_params) == 1:
1.195 + num_parameters = first(max_params)
1.196 + num_defaults = first(max_params) - first(min_params)
1.197 +
1.198 + # Some information about the target may be available and be used to
1.199 + # provide warnings about argument compatibility.
1.200 +
1.201 + if self.importer.give_warning("args"):
1.202 + unsuitable = self.get_referenced_attribute_invocations(location)
1.203 +
1.204 + if unsuitable:
1.205 + for ref in unsuitable:
1.206 + _objpath = ref.get_origin()
1.207 + print >>sys.stderr, \
1.208 + "In %s, at line %d, inappropriate number of " \
1.209 + "arguments given. Need %d arguments to call %s." % (
1.210 + self.get_namespace_path(), n.lineno,
1.211 + len(self.importer.function_parameters[_objpath]),
1.212 + _objpath)
1.213
1.214 # Determine any readily-accessible target identity.
1.215
1.216 @@ -1120,16 +1198,19 @@
1.217 if not target_identity:
1.218 self.record_temp("__tmp_targets")
1.219
1.220 - if context_identity and context_identity.startswith("__tmp_contexts"):
1.221 - self.record_temp("__tmp_contexts")
1.222 + if context_identity:
1.223 + if context_identity.startswith("__tmp_contexts"):
1.224 + self.record_temp("__tmp_contexts")
1.225
1.226 # Arguments are presented in a temporary frame array with any context
1.227 # always being the first argument. Where it would be unused, it may be
1.228 # set to null.
1.229
1.230 + known_parameters = num_parameters is not None
1.231 +
1.232 if context_required:
1.233 if have_access_context:
1.234 - args = ["__ATTRVALUE(%s)" % context_identity]
1.235 + args = [context_identity]
1.236 else:
1.237 args = ["__CONTEXT_AS_VALUE(%s)" % context_var]
1.238 else:
1.239 @@ -1138,7 +1219,7 @@
1.240 # Complete the array with null values, permitting tests for a complete
1.241 # set of arguments.
1.242
1.243 - args += [None] * (parameters is None and len(n.args) or parameters is not None and len(parameters) or 0)
1.244 + args += [None] * (num_parameters is None and len(n.args) or num_parameters is not None and num_parameters or 0)
1.245 kwcodes = []
1.246 kwargs = []
1.247
1.248 @@ -1198,18 +1279,29 @@
1.249
1.250 # Defaults are added to the frame where arguments are missing.
1.251
1.252 - if parameters:
1.253 - function_defaults = self.importer.function_defaults.get(objpath)
1.254 - if function_defaults:
1.255 -
1.256 - # Visit each default and set any missing arguments.
1.257 - # Use the target structure to obtain defaults, as opposed to the
1.258 - # actual function involved.
1.259 -
1.260 - for i, (argname, default) in enumerate(function_defaults):
1.261 - argnum = parameters.index(argname)
1.262 - if not args[argnum+1]:
1.263 - args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
1.264 + if parameters and function_defaults:
1.265 +
1.266 + # Visit each default and set any missing arguments. Where keyword
1.267 + # arguments have been used, the defaults must be inspected and, if
1.268 + # necessary, inserted into gaps in the argument list.
1.269 +
1.270 + for i, (argname, default) in enumerate(function_defaults):
1.271 + argnum = parameters.index(argname)
1.272 + if not args[argnum+1]:
1.273 + args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
1.274 +
1.275 + elif known_parameters:
1.276 +
1.277 + # No specific parameter details are provided, but no keyword
1.278 + # arguments are used. Thus, defaults can be supplied using position
1.279 + # information only.
1.280 +
1.281 + i = len(n.args)
1.282 + pos = i - (num_parameters - num_defaults)
1.283 + while i < num_parameters:
1.284 + args[i+1] = "__GETDEFAULT(%s.value, %d)" % (target_var, pos)
1.285 + i += 1
1.286 + pos += 1
1.287
1.288 # Test for missing arguments.
1.289
1.290 @@ -1264,10 +1356,19 @@
1.291 stages.append("__get_function(%s, %s)" % (
1.292 context_identity, target_var))
1.293 else:
1.294 - stages.append("__get_function(__CONTEXT_AS_VALUE(%s).value, %s)" % (
1.295 + stages.append("__get_function(__CONTEXT_AS_VALUE(%s), %s)" % (
1.296 context_var, target_var))
1.297 else:
1.298 - stages.append("__load_via_object(%s.value, __fn__).fn" % target_var)
1.299 + stages.append("__load_via_object(__VALUE(%s), __fn__).fn" % target_var)
1.300 +
1.301 + # With known parameters, the target can be tested.
1.302 +
1.303 + elif known_parameters:
1.304 + context_arg = context_required and args[0] or "__NULL"
1.305 + if self.always_callable(refs):
1.306 + stages.append("__get_function(%s, %s)" % (context_arg, target_var))
1.307 + else:
1.308 + stages.append("__check_and_get_function(%s, %s)" % (context_arg, target_var))
1.309
1.310 # With a known target, the function is obtained directly and called.
1.311 # By putting the invocation at the end of the final element in the
1.312 @@ -1275,7 +1376,7 @@
1.313 # the sequence. Moreover, the parameters become part of the sequence
1.314 # and thereby participate in a guaranteed evaluation order.
1.315
1.316 - if target or function:
1.317 + if target or function or known_parameters:
1.318 stages[-1] += "(%s)" % argstr
1.319 if instantiation:
1.320 return InstantiationResult(instantiation, stages)
1.321 @@ -1288,7 +1389,7 @@
1.322 else:
1.323 stages.append("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % (
1.324 target_var,
1.325 - self.always_callable and 1 or 0,
1.326 + self.always_callable(refs) and 1 or 0,
1.327 len(kwargs), kwcodestr, kwargstr,
1.328 len(args), "__ARGS(%s)" % argstr))
1.329 return InvocationResult(stages)
1.330 @@ -1304,13 +1405,13 @@
1.331
1.332 "Determine whether all 'refs' are callable."
1.333
1.334 + if not refs:
1.335 + return False
1.336 +
1.337 for ref in refs:
1.338 - if not ref.static():
1.339 + if not ref.has_kind("<function>") and not self.importer.get_attributes(ref, "__fn__"):
1.340 return False
1.341 - else:
1.342 - origin = ref.final()
1.343 - if not self.importer.get_attribute(origin, "__fn__"):
1.344 - return False
1.345 +
1.346 return True
1.347
1.348 def need_default_arguments(self, objpath, nargs):
1.349 @@ -1323,6 +1424,31 @@
1.350 parameters = self.importer.function_parameters.get(objpath)
1.351 return nargs < len(parameters)
1.352
1.353 + def uses_keyword_arguments(self, n):
1.354 +
1.355 + "Return whether invocation node 'n' uses keyword arguments."
1.356 +
1.357 + for arg in enumerate(n.args):
1.358 + if isinstance(arg, compiler.ast.Keyword):
1.359 + return True
1.360 +
1.361 + return False
1.362 +
1.363 + def get_attributes_for_attrname(self, attrname):
1.364 +
1.365 + "Return a set of all attributes exposed by 'attrname'."
1.366 +
1.367 + usage = [(attrname, True, False)]
1.368 + class_types = self.deducer.get_class_types_for_usage(usage)
1.369 + instance_types = self.deducer.get_instance_types_for_usage(usage)
1.370 + module_types = self.deducer.get_module_types_for_usage(usage)
1.371 + attrs = set()
1.372 +
1.373 + for ref in combine_types(class_types, instance_types, module_types):
1.374 + attrs.update(self.importer.get_attributes(ref, attrname))
1.375 +
1.376 + return attrs
1.377 +
1.378 def process_lambda_node(self, n):
1.379
1.380 "Process the given lambda node 'n'."
1.381 @@ -1342,7 +1468,7 @@
1.382
1.383 else:
1.384 self.record_temp("__tmp_value")
1.385 - return make_expression("(__tmp_value = __COPY(&%s, sizeof(%s)), %s, __ATTRVALUE(__tmp_value))" % (
1.386 + return make_expression("(__tmp_value = __ATTRVALUE(__COPY(&%s, sizeof(%s))), %s, __tmp_value)" % (
1.387 encode_path(function_name),
1.388 encode_symbol("obj", function_name),
1.389 ", ".join(defaults)))
1.390 @@ -1923,16 +2049,16 @@
1.391 if self.uses_temp(name, "__tmp_targets"):
1.392 self.writeline("__attr __tmp_targets[%d];" % targets)
1.393 if self.uses_temp(name, "__tmp_contexts"):
1.394 - self.writeline("__ref __tmp_contexts[%d];" % targets)
1.395 + self.writeline("__attr __tmp_contexts[%d];" % targets)
1.396
1.397 # Add temporary variable usage details.
1.398
1.399 if self.uses_temp(name, "__tmp_private_context"):
1.400 - self.writeline("__ref __tmp_private_context;")
1.401 + self.writeline("__attr __tmp_private_context;")
1.402 if self.uses_temp(name, "__tmp_value"):
1.403 - self.writeline("__ref __tmp_value;")
1.404 + self.writeline("__attr __tmp_value;")
1.405 if self.uses_temp(name, "__tmp_target_value"):
1.406 - self.writeline("__ref __tmp_target_value;")
1.407 + self.writeline("__attr __tmp_target_value;")
1.408 if self.uses_temp(name, "__tmp_result"):
1.409 self.writeline("__attr __tmp_result;")
1.410