1.1 --- a/translator.py Mon Mar 20 19:07:01 2017 +0100
1.2 +++ b/translator.py Mon Mar 20 19:13:18 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 @@ -1067,6 +1067,7 @@
1.13
1.14 elif objpath:
1.15 parameters = self.importer.function_parameters.get(objpath)
1.16 + function_defaults = self.importer.function_defaults.get(objpath)
1.17
1.18 # Class invocation involves instantiators.
1.19
1.20 @@ -1102,22 +1103,54 @@
1.21
1.22 target_structure = "&%s" % encode_path(objpath)
1.23
1.24 - # Other targets are retrieved at run-time. Some information about them
1.25 - # may be available and be used to provide warnings about argument
1.26 - # compatibility.
1.27 -
1.28 - elif self.importer.give_warning("args"):
1.29 - unsuitable = self.get_referenced_attribute_invocations(location)
1.30 -
1.31 - if unsuitable:
1.32 - for ref in unsuitable:
1.33 - _objpath = ref.get_origin()
1.34 - num_parameters = len(self.importer.function_parameters[_objpath])
1.35 - print >>sys.stderr, \
1.36 - "In %s, at line %d, inappropriate number of " \
1.37 - "arguments given. Need %d arguments to call %s." % (
1.38 - self.get_namespace_path(), n.lineno, num_parameters,
1.39 - _objpath)
1.40 + # Other targets are retrieved at run-time.
1.41 +
1.42 + else:
1.43 + if location:
1.44 + path, name, attrnames, access_number = location
1.45 + attrname = attrnames and attrnames.rsplit(".", 1)[-1]
1.46 +
1.47 + # Determine any common aspects of any attribute.
1.48 +
1.49 + if attrname:
1.50 + all_params = set()
1.51 + all_defaults = set()
1.52 + refs = set()
1.53 +
1.54 + # Obtain parameters and defaults for each possible target.
1.55 +
1.56 + for ref in self.get_attributes_for_attrname(attrname):
1.57 + refs.add(ref)
1.58 + origin = ref.get_origin()
1.59 + params = self.importer.function_parameters.get(origin)
1.60 + if params:
1.61 + all_params.add(tuple(params))
1.62 + defaults = self.importer.function_defaults.get(origin)
1.63 + if defaults:
1.64 + all_defaults.add(tuple(defaults))
1.65 +
1.66 + # Where the parameters and defaults are always the same,
1.67 + # permit populating them in advance.
1.68 +
1.69 + if len(all_params) == 1 and (not all_defaults or len(all_defaults) == 1):
1.70 + parameters = first(all_params)
1.71 + function_defaults = all_defaults and first(all_defaults) or []
1.72 +
1.73 + # Some information about the target may be available and be used to
1.74 + # provide warnings about argument compatibility.
1.75 +
1.76 + if self.importer.give_warning("args"):
1.77 + unsuitable = self.get_referenced_attribute_invocations(location)
1.78 +
1.79 + if unsuitable:
1.80 + for ref in unsuitable:
1.81 + _objpath = ref.get_origin()
1.82 + num_parameters = len(self.importer.function_parameters[_objpath])
1.83 + print >>sys.stderr, \
1.84 + "In %s, at line %d, inappropriate number of " \
1.85 + "arguments given. Need %d arguments to call %s." % (
1.86 + self.get_namespace_path(), n.lineno, num_parameters,
1.87 + _objpath)
1.88
1.89 # Determine any readily-accessible target identity.
1.90
1.91 @@ -1209,18 +1242,17 @@
1.92
1.93 # Defaults are added to the frame where arguments are missing.
1.94
1.95 - if parameters:
1.96 - function_defaults = self.importer.function_defaults.get(objpath)
1.97 - if function_defaults:
1.98 -
1.99 - # Visit each default and set any missing arguments.
1.100 - # Use the target structure to obtain defaults, as opposed to the
1.101 - # actual function involved.
1.102 -
1.103 - for i, (argname, default) in enumerate(function_defaults):
1.104 - argnum = parameters.index(argname)
1.105 - if not args[argnum+1]:
1.106 - args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
1.107 + if parameters and function_defaults:
1.108 + target_structure = target_structure or "%s.value" % target_var
1.109 +
1.110 + # Visit each default and set any missing arguments.
1.111 + # Use the target structure to obtain defaults, as opposed to the
1.112 + # actual function involved.
1.113 +
1.114 + for i, (argname, default) in enumerate(function_defaults):
1.115 + argnum = parameters.index(argname)
1.116 + if not args[argnum+1]:
1.117 + args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
1.118
1.119 # Test for missing arguments.
1.120
1.121 @@ -1281,13 +1313,25 @@
1.122 else:
1.123 stages.append("__load_via_object(%s.value, __fn__).fn" % target_var)
1.124
1.125 + # With known parameters, the target can be tested.
1.126 +
1.127 + elif parameters:
1.128 + context_arg = context_required and args[0] or "__NULL"
1.129 + if self.always_callable(refs):
1.130 + if context_var == target_var:
1.131 + stages.append("__get_function_unchecked(%s)" % target_var)
1.132 + else:
1.133 + stages.append("__get_function(%s.value, %s)" % (context_arg, target_var))
1.134 + else:
1.135 + stages.append("__check_and_get_function(%s.value, %s)" % (context_arg, target_var))
1.136 +
1.137 # With a known target, the function is obtained directly and called.
1.138 # By putting the invocation at the end of the final element in the
1.139 # instruction sequence (the stages), the result becomes the result of
1.140 # the sequence. Moreover, the parameters become part of the sequence
1.141 # and thereby participate in a guaranteed evaluation order.
1.142
1.143 - if target or function:
1.144 + if target or function or parameters:
1.145 stages[-1] += "(%s)" % argstr
1.146 if instantiation:
1.147 return InstantiationResult(instantiation, stages)
1.148 @@ -1335,6 +1379,21 @@
1.149 parameters = self.importer.function_parameters.get(objpath)
1.150 return nargs < len(parameters)
1.151
1.152 + def get_attributes_for_attrname(self, attrname):
1.153 +
1.154 + "Return a set of all attributes exposed by 'attrname'."
1.155 +
1.156 + usage = [(attrname, True, False)]
1.157 + class_types = self.deducer.get_class_types_for_usage(usage)
1.158 + instance_types = self.deducer.get_instance_types_for_usage(usage)
1.159 + module_types = self.deducer.get_module_types_for_usage(usage)
1.160 + attrs = set()
1.161 +
1.162 + for ref in combine_types(class_types, instance_types, module_types):
1.163 + attrs.update(self.importer.get_attributes(ref, attrname))
1.164 +
1.165 + return attrs
1.166 +
1.167 def process_lambda_node(self, n):
1.168
1.169 "Process the given lambda node 'n'."