1.1 --- a/deducer.py Thu Nov 24 23:42:19 2016 +0100
1.2 +++ b/deducer.py Fri Nov 25 00:53:22 2016 +0100
1.3 @@ -391,7 +391,7 @@
1.4
1.5 location " " name " " test " " test type " " base " " traversed attributes
1.6 " " attributes to traverse " " context " " access method
1.7 - " " static attribute
1.8 + " " static attribute " " accessor kinds
1.9 """
1.10
1.11 f_attrs = open(join(self.output, "attribute_plans"), "w")
1.12 @@ -401,9 +401,11 @@
1.13 locations.sort()
1.14
1.15 for location in locations:
1.16 - name, test, test_type, base, traversed, traversal_modes, attrnames, \
1.17 + name, test, test_type, base, \
1.18 + traversed, traversal_modes, attrnames, \
1.19 context, context_test, \
1.20 - first_method, final_method, attr = self.access_plans[location]
1.21 + first_method, final_method, \
1.22 + attr, accessor_kinds = self.access_plans[location]
1.23
1.24 print >>f_attrs, encode_access_location(location), \
1.25 name or "{}", \
1.26 @@ -413,7 +415,8 @@
1.27 ".".join(traversal_modes) or "{}", \
1.28 ".".join(attrnames) or "{}", \
1.29 context, context_test, \
1.30 - first_method, final_method, attr or "{}"
1.31 + first_method, final_method, attr or "{}", \
1.32 + ",".join(accessor_kinds)
1.33
1.34 finally:
1.35 f_attrs.close()
1.36 @@ -2099,6 +2102,10 @@
1.37 (base and "base" or "original-accessor") or \
1.38 "final-accessor"
1.39
1.40 - return name, test, test_type, base, traversed, traversal_modes, remaining, context, context_test, first_method, final_method, origin
1.41 + return name, test, test_type, base, \
1.42 + traversed, traversal_modes, remaining, \
1.43 + context, context_test, \
1.44 + first_method, final_method, \
1.45 + origin, accessor_kinds
1.46
1.47 # vim: tabstop=4 expandtab shiftwidth=4
2.1 --- a/optimiser.py Thu Nov 24 23:42:19 2016 +0100
2.2 +++ b/optimiser.py Fri Nov 25 00:53:22 2016 +0100
2.3 @@ -57,6 +57,7 @@
2.4 # Specific attribute access information.
2.5
2.6 self.access_instructions = {}
2.7 + self.accessor_kinds = {}
2.8
2.9 # Object structure information.
2.10
2.11 @@ -343,8 +344,11 @@
2.12
2.13 # Obtain the access details.
2.14
2.15 - name, test, test_type, base, traversed, traversal_modes, \
2.16 - attrnames, context, context_test, first_method, final_method, origin = access_plan
2.17 + name, test, test_type, base, \
2.18 + traversed, traversal_modes, attrnames, \
2.19 + context, context_test, \
2.20 + first_method, final_method, \
2.21 + origin, accessor_kinds = access_plan
2.22
2.23 instructions = []
2.24 emit = instructions.append
2.25 @@ -539,6 +543,7 @@
2.26 emit(accessor)
2.27
2.28 self.access_instructions[access_location] = instructions
2.29 + self.accessor_kinds[access_location] = accessor_kinds
2.30
2.31 def get_ambiguity_for_attributes(self, attrnames):
2.32
3.1 --- a/tests/methods_unbound.py Thu Nov 24 23:42:19 2016 +0100
3.2 +++ b/tests/methods_unbound.py Fri Nov 25 00:53:22 2016 +0100
3.3 @@ -1,15 +1,39 @@
3.4 class C:
3.5 + def __init__(self):
3.6 + self.a = 1
3.7 def m(self, x):
3.8 return x
3.9
3.10 def f(obj, i):
3.11 if i:
3.12 - return obj.m(i)
3.13 + return obj.m(i) # should cause access to an unbound method
3.14 + else:
3.15 + return obj.m
3.16 +
3.17 +def g(obj, i):
3.18 + obj.a # only provided by instances of C
3.19 + if i:
3.20 + return obj.m(i) # should use the method directly since obj is an instance
3.21 else:
3.22 return obj.m
3.23
3.24 c = C()
3.25 -#print f(C, 1) # NOTE: Need to raise and handle error.
3.26 +
3.27 +try:
3.28 + print f(C, 1) # fails
3.29 +except UnboundMethodInvocation:
3.30 + print "f(C, 1): Unbound method is not callable."
3.31 +
3.32 fn = f(C, 0)
3.33 -print get_using(fn, c)(2) # 2
3.34 -print get_using(f(C, 0), c)(2) # 2
3.35 +
3.36 +try:
3.37 + print fn(2) # fails
3.38 +except UnboundMethodInvocation:
3.39 + print "fn(2): Unbound method is not callable."
3.40 +
3.41 +print get_using(fn, c)(2) # 2
3.42 +print get_using(f(C, 0), c)(2) # 2
3.43 +
3.44 +#print g(C, 1) # should fail with an error caused by a guard
3.45 +print g(c, 1) # 1
3.46 +print g(c, 0)(3) # 3
4.1 --- a/translator.py Thu Nov 24 23:42:19 2016 +0100
4.2 +++ b/translator.py Fri Nov 25 00:53:22 2016 +0100
4.3 @@ -55,7 +55,8 @@
4.4
4.5 "An abstract translation result mix-in."
4.6
4.7 - pass
4.8 + def get_accessor_kinds(self):
4.9 + return None
4.10
4.11 class ReturnRef(TranslationResult):
4.12
4.13 @@ -156,9 +157,10 @@
4.14
4.15 "A translation result for an attribute access."
4.16
4.17 - def __init__(self, s, refs):
4.18 + def __init__(self, s, refs, accessor_kinds):
4.19 Expression.__init__(self, s)
4.20 self.refs = refs
4.21 + self.accessor_kinds = accessor_kinds
4.22
4.23 def get_origin(self):
4.24 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
4.25 @@ -171,6 +173,9 @@
4.26 return True
4.27 return False
4.28
4.29 + def get_accessor_kinds(self):
4.30 + return self.accessor_kinds
4.31 +
4.32 def __repr__(self):
4.33 return "AttrResult(%r, %r)" % (self.s, self.get_origin())
4.34
4.35 @@ -653,7 +658,7 @@
4.36 out = "(\n%s\n)" % ",\n".join(output)
4.37
4.38 del self.attrs[0]
4.39 - return AttrResult(out, refs)
4.40 + return AttrResult(out, refs, self.get_accessor_kinds(location))
4.41
4.42 def get_referenced_attributes(self, location):
4.43
4.44 @@ -668,6 +673,12 @@
4.45 refs.append(attr)
4.46 return refs
4.47
4.48 + def get_accessor_kinds(self, location):
4.49 +
4.50 + "Return the accessor kinds for 'location'."
4.51 +
4.52 + return self.optimiser.accessor_kinds[location]
4.53 +
4.54 def get_access_location(self, name):
4.55
4.56 """
4.57 @@ -869,12 +880,24 @@
4.58
4.59 elif objpath:
4.60 parameters = self.importer.function_parameters.get(objpath)
4.61 +
4.62 + # Class invocation involves instantiators.
4.63 +
4.64 if expr.has_kind("<class>"):
4.65 target = encode_instantiator_pointer(objpath)
4.66 target_structure = encode_initialiser_pointer(objpath)
4.67 +
4.68 + # Only plain functions and bound methods employ function pointers.
4.69 +
4.70 elif expr.has_kind("<function>"):
4.71 - target = encode_function_pointer(objpath)
4.72 - target_structure = encode_path(objpath)
4.73 +
4.74 + # Test for functions and methods.
4.75 +
4.76 + accessor_kinds = expr.get_accessor_kinds()
4.77 +
4.78 + if not self.is_method(objpath) or accessor_kinds and len(accessor_kinds) == 1 and first(accessor_kinds) == "<instance>":
4.79 + target = encode_function_pointer(objpath)
4.80 + target_structure = encode_path(objpath)
4.81
4.82 # Other targets are retrieved at run-time.
4.83