1.1 --- a/encoders.py Thu Jul 12 18:19:02 2018 +0200
1.2 +++ b/encoders.py Sun Jul 22 00:19:35 2018 +0200
1.3 @@ -259,6 +259,14 @@
1.4 "__load_static_ignore", "__load_static_replace", "__load_static_test", "<test_context_static>",
1.5 )
1.6
1.7 +accessor_values = (
1.8 + "<accessor>",
1.9 + )
1.10 +
1.11 +accessor_ops = (
1.12 + "<accessor>", "<set_accessor>",
1.13 + )
1.14 +
1.15 context_values = (
1.16 "<context>",
1.17 )
1.18 @@ -278,7 +286,7 @@
1.19 "<accessor>", "<context>", "<name>", "<private_context>", "<target_accessor>"
1.20 )
1.21
1.22 -def encode_access_instruction(instruction, subs, context_index):
1.23 +def encode_access_instruction(instruction, subs, accessor_index, context_index):
1.24
1.25 """
1.26 Encode the 'instruction' - a sequence starting with an operation and
1.27 @@ -288,6 +296,9 @@
1.28 The 'subs' parameter defines a mapping of substitutions for special values
1.29 used in instructions.
1.30
1.31 + The 'accessor_index' parameter defines the position in local accessor
1.32 + storage for the referenced accessor or affected by an accessor operation.
1.33 +
1.34 The 'context_index' parameter defines the position in local context storage
1.35 for the referenced context or affected by a context operation.
1.36
1.37 @@ -304,7 +315,7 @@
1.38 if args:
1.39 converting_op = op
1.40 for arg in args:
1.41 - s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, context_index)
1.42 + s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, accessor_index, context_index)
1.43 substituted.update(_substituted)
1.44 a.append(s)
1.45 converting_op = None
1.46 @@ -326,6 +337,11 @@
1.47 elif op in static_ops:
1.48 a[-1] = "&%s" % a[-1]
1.49
1.50 + # Add accessor storage information to certain operations.
1.51 +
1.52 + if op in accessor_ops:
1.53 + a.insert(0, accessor_index)
1.54 +
1.55 # Add context storage information to certain operations.
1.56
1.57 if op in context_ops:
1.58 @@ -365,19 +381,19 @@
1.59
1.60 return "%s%s" % (op, argstr), substituted
1.61
1.62 -def encode_access_instruction_arg(arg, subs, op, context_index):
1.63 +def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index):
1.64
1.65 """
1.66 Encode 'arg' using 'subs' to define substitutions, 'op' to indicate the
1.67 - operation to which the argument belongs, and 'context_index' to indicate any
1.68 - affected context storage.
1.69 + operation to which the argument belongs, and with 'accessor_index' and
1.70 + 'context_index' indicating any affected accessor and context storage.
1.71
1.72 Return a tuple containing the encoded form of 'arg' along with a collection
1.73 of any substituted values.
1.74 """
1.75
1.76 if isinstance(arg, tuple):
1.77 - encoded, substituted = encode_access_instruction(arg, subs, context_index)
1.78 + encoded, substituted = encode_access_instruction(arg, subs, accessor_index, context_index)
1.79 return attribute_to_reference(op, arg[0], encoded, substituted)
1.80
1.81 # Special values only need replacing, not encoding.
1.82 @@ -386,7 +402,7 @@
1.83
1.84 # Handle values modified by storage details.
1.85
1.86 - if arg in context_values:
1.87 + if arg in accessor_values or arg in context_values:
1.88 encoded = "%s(%s)" % (subs.get(arg), context_index)
1.89 else:
1.90 encoded = subs.get(arg)
2.1 --- a/lib/__builtins__/tuple.py Thu Jul 12 18:19:02 2018 +0200
2.2 +++ b/lib/__builtins__/tuple.py Sun Jul 22 00:19:35 2018 +0200
2.3 @@ -22,7 +22,8 @@
2.4 from __builtins__.iteration.iterator import itemiterator
2.5 from __builtins__.sequence import hashable, unpackable
2.6 from native import tuple_init, \
2.7 - list_element, list_len, list_setsize, list_setelement
2.8 + list_element, list_len, list_setsize, list_setelement, \
2.9 + isinstance as _isinstance
2.10
2.11 class tuple(unpackable, hashable):
2.12
2.13 @@ -73,7 +74,13 @@
2.14
2.15 return list_len(self.__data__)
2.16
2.17 - def __add__(self, other): pass
2.18 + def __add__(self, other):
2.19 +
2.20 + "Add this tuple to 'other'."
2.21 +
2.22 + if not _isinstance(other, tuple):
2.23 + raise TypeError
2.24 + return tuple(tuplepair(self, other))
2.25
2.26 def __str__(self):
2.27
2.28 @@ -110,4 +117,28 @@
2.29
2.30 raise TypeError
2.31
2.32 +class tuplepair:
2.33 +
2.34 + "A combination of tuples."
2.35 +
2.36 + def __init__(self, a, b):
2.37 + self.a = a
2.38 + self.b = b
2.39 +
2.40 + def __len__(self):
2.41 +
2.42 + "Return the combined length of the tuples."
2.43 +
2.44 + return len(self.a) + len(self.b)
2.45 +
2.46 + def __getitem__(self, index):
2.47 +
2.48 + "Return the value from 'index' in the combined tuple."
2.49 +
2.50 + asize = len(self.a)
2.51 + if index < asize:
2.52 + return self.a.__get_single_item__(index)
2.53 + else:
2.54 + return self.b.__get_single_item__(index - asize)
2.55 +
2.56 # vim: tabstop=4 expandtab shiftwidth=4
3.1 --- a/templates/ops.h Thu Jul 12 18:19:02 2018 +0200
3.2 +++ b/templates/ops.h Sun Jul 22 00:19:35 2018 +0200
3.3 @@ -107,10 +107,11 @@
3.4 __attr __test_context_revert(int target, __attr context, __attr attr, __attr contexts[]);
3.5 __attr __test_context_static(int target, __attr context, __ref value, __attr contexts[]);
3.6
3.7 +#define __get_accessor(__TARGET) (__tmp_values[__TARGET])
3.8 #define __get_context(__TARGET) (__tmp_contexts[__TARGET])
3.9 #define __set_context(__TARGET, __ATTR) (__tmp_contexts[__TARGET] = (__ATTR))
3.10 #define __set_private_context(__ATTR) (__tmp_private_context = (__ATTR))
3.11 -#define __set_accessor(__ATTR) (__tmp_value = (__ATTR))
3.12 +#define __set_accessor(__TARGET, __ATTR) (__tmp_values[__TARGET] = (__ATTR))
3.13 #define __set_target_accessor(__ATTR) (__tmp_target_value = (__ATTR))
3.14
3.15 /* Context testing for invocations. */
4.1 --- a/tests/methods_rebound.py Thu Jul 12 18:19:02 2018 +0200
4.2 +++ b/tests/methods_rebound.py Sun Jul 22 00:19:35 2018 +0200
4.3 @@ -22,17 +22,30 @@
4.4
4.5 e = E()
4.6
4.7 +# Normal method access and invocation.
4.8 +
4.9 print c.f.__name__ # f
4.10 print c.f() # <__main__.C instance>
4.11 # 123
4.12 +
4.13 +# Access and call assigned bound method.
4.14 +
4.15 print d.f.__name__ # wrapper
4.16 print d.f() # <__main__.C instance>
4.17 # 123
4.18
4.19 +# Access and call assigned function.
4.20 +
4.21 print e.f.__name__ # fn
4.22 print e.f() # 456
4.23 +
4.24 +# Access details of assigned method.
4.25 +
4.26 print e.g.__name__ # f
4.27
4.28 +# Attempt to call method belonging to another class via an incompatible
4.29 +# instance. In Python, this would be an unbound method call attempt.
4.30 +
4.31 try:
4.32 print e.g()
4.33 except TypeError:
5.1 --- a/tests/nested_calls.py Thu Jul 12 18:19:02 2018 +0200
5.2 +++ b/tests/nested_calls.py Sun Jul 22 00:19:35 2018 +0200
5.3 @@ -13,3 +13,31 @@
5.4
5.5 c = C(3)
5.6 print c.length() # 6
5.7 +
5.8 +# Explicit function for addition purposes.
5.9 +
5.10 +def combine(x, y, z):
5.11 + return x + y + z
5.12 +
5.13 +class Tree:
5.14 + def __init__(self, item, left=None, right=None):
5.15 + self.item = item
5.16 + self.left = left
5.17 + self.right = right
5.18 +
5.19 + def count(self):
5.20 + if self.left and self.right:
5.21 + # Test calls in parameter lists needing separate temporary storage.
5.22 + return combine(self.item, self.left.count(), self.right.count())
5.23 + else:
5.24 + return self.item
5.25 +
5.26 +tree = \
5.27 + Tree(10000,
5.28 + Tree(2000,
5.29 + Tree(300),
5.30 + Tree(40)
5.31 + ),
5.32 + Tree(5))
5.33 +
5.34 +print tree.count() # 12345
6.1 --- a/tests/tuple.py Thu Jul 12 18:19:02 2018 +0200
6.2 +++ b/tests/tuple.py Sun Jul 22 00:19:35 2018 +0200
6.3 @@ -31,3 +31,19 @@
6.4 a, b, c = l
6.5 except ValueError, exc:
6.6 print "a, b, c = l: failed with length", exc.value
6.7 +
6.8 +# Add tuples together.
6.9 +
6.10 +m = ("five", 6, 7, 8)
6.11 +n = t + m
6.12 +print n # (1, 2, 3, "four", "five", 6, 7, 8)
6.13 +
6.14 +# Add to list, really testing tuple iteration.
6.15 +
6.16 +o = l + m
6.17 +print o # [1, 2, 3, "four", "five", 6, 7, 8]
6.18 +
6.19 +try:
6.20 + print t + l # should raise an exception
6.21 +except TypeError:
6.22 + print "t + l: failed due to tuple-list addition"
7.1 --- a/translator.py Thu Jul 12 18:19:02 2018 +0200
7.2 +++ b/translator.py Sun Jul 22 00:19:35 2018 +0200
7.3 @@ -115,6 +115,7 @@
7.4
7.5 self.namespaces = []
7.6 self.in_conditional = False
7.7 + self.in_parameter_list = False
7.8
7.9 # Exception raising adjustments.
7.10
7.11 @@ -317,6 +318,8 @@
7.12 self.max_function_target = 0
7.13 self.context_index = 0
7.14 self.max_context_index = 0
7.15 + self.accessor_index = 0
7.16 + self.max_accessor_index = 0
7.17 self.start_module()
7.18 self.process_structure(node)
7.19 self.end_module()
7.20 @@ -554,11 +557,13 @@
7.21 # The context set or retrieved will be that used by any enclosing
7.22 # invocation.
7.23
7.24 + accessor_index = self.accessor_index
7.25 context_index = self.context_index
7.26 context_identity = None
7.27 context_identity_verified = False
7.28 final_identity = None
7.29 accessor_test = False
7.30 + accessor_stored = False
7.31
7.32 # Obtain encoded versions of each instruction, accumulating temporary
7.33 # variables.
7.34 @@ -568,7 +573,9 @@
7.35 # Intercept a special instruction identifying the context.
7.36
7.37 if instruction[0] in ("<context_identity>", "<context_identity_verified>"):
7.38 - context_identity, _substituted = encode_access_instruction_arg(instruction[1], subs, instruction[0], context_index)
7.39 + context_identity, _substituted = \
7.40 + encode_access_instruction_arg(instruction[1], subs, instruction[0],
7.41 + accessor_index, context_index)
7.42 context_identity_verified = instruction[0] == "<context_identity_verified>"
7.43 continue
7.44
7.45 @@ -585,10 +592,16 @@
7.46 instruction = ("__to_error", instruction)
7.47 accessor_test = True
7.48
7.49 + # Intercept accessor storage.
7.50 +
7.51 + elif instruction[0] == "<set_accessor>":
7.52 + accessor_stored = True
7.53 +
7.54 # Collect the encoded instruction, noting any temporary variables
7.55 # required by it.
7.56
7.57 - encoded, _substituted = encode_access_instruction(instruction, subs, context_index)
7.58 + encoded, _substituted = encode_access_instruction(instruction, subs,
7.59 + accessor_index, context_index)
7.60 output.append(encoded)
7.61 substituted.update(_substituted)
7.62
7.63 @@ -604,7 +617,9 @@
7.64 refs = set([self.importer.identify(final_identity)])
7.65
7.66 del self.attrs[0]
7.67 - return AttrResult(output, refs, location, context_identity, context_identity_verified, accessor_test)
7.68 + return AttrResult(output, refs, location,
7.69 + context_identity, context_identity_verified,
7.70 + accessor_test, accessor_stored)
7.71
7.72 def init_substitutions(self):
7.73
7.74 @@ -619,21 +634,22 @@
7.75 # Substitutions used by instructions.
7.76
7.77 "<private_context>" : "__tmp_private_context",
7.78 - "<accessor>" : "__tmp_value",
7.79 "<target_accessor>" : "__tmp_target_value",
7.80
7.81 # Mappings to be replaced by those given below.
7.82
7.83 + "<accessor>" : "__tmp_values",
7.84 "<context>" : "__tmp_contexts",
7.85 "<test_context_revert>" : "__tmp_contexts",
7.86 "<test_context_static>" : "__tmp_contexts",
7.87 "<set_context>" : "__tmp_contexts",
7.88 "<set_private_context>" : "__tmp_private_context",
7.89 - "<set_accessor>" : "__tmp_value",
7.90 + "<set_accessor>" : "__tmp_values",
7.91 "<set_target_accessor>" : "__tmp_target_value",
7.92 }
7.93
7.94 self.op_subs = {
7.95 + "<accessor>" : "__get_accessor",
7.96 "<context>" : "__get_context",
7.97 "<test_context_revert>" : "__test_context_revert",
7.98 "<test_context_static>" : "__test_context_static",
7.99 @@ -840,6 +856,8 @@
7.100 self.max_function_target = 0
7.101 self.context_index = 0
7.102 self.max_context_index = 0
7.103 + self.accessor_index = 0
7.104 + self.max_accessor_index = 0
7.105
7.106 # Volatile locals for exception handling.
7.107
7.108 @@ -1082,6 +1100,7 @@
7.109 # With such operations present, the expression cannot be eliminated.
7.110
7.111 tests_accessor = have_access_context and expr.tests_accessor()
7.112 + stores_accessor = have_access_context and expr.stores_accessor()
7.113
7.114 # Parameter details and parameter list dimensions.
7.115
7.116 @@ -1258,6 +1277,9 @@
7.117 if need_context_stored:
7.118 self.record_temp("__tmp_contexts")
7.119
7.120 + if stores_accessor:
7.121 + self.record_temp("__tmp_values")
7.122 +
7.123 # Arguments are presented in a temporary frame array with any context
7.124 # always being the first argument. Where it would be unused, it may be
7.125 # set to null.
7.126 @@ -1284,6 +1306,7 @@
7.127
7.128 function_target = self.function_target
7.129 context_index = self.context_index
7.130 + accessor_index = self.accessor_index
7.131
7.132 if need_target_stored:
7.133 self.next_target()
7.134 @@ -1291,6 +1314,12 @@
7.135 if need_context_stored:
7.136 self.next_context()
7.137
7.138 + if stores_accessor:
7.139 + self.next_accessor()
7.140 +
7.141 + in_parameter_list = self.in_parameter_list
7.142 + self.in_parameter_list = True
7.143 +
7.144 for i, arg in enumerate(n.args):
7.145 argexpr = self.process_structure_node(arg)
7.146
7.147 @@ -1329,8 +1358,12 @@
7.148
7.149 # Reference the current target again.
7.150
7.151 - self.function_target = function_target
7.152 - self.context_index = context_index
7.153 + self.in_parameter_list = in_parameter_list
7.154 +
7.155 + if not self.in_parameter_list:
7.156 + self.function_target = function_target
7.157 + self.context_index = context_index
7.158 + self.accessor_index = accessor_index
7.159
7.160 # Defaults are added to the frame where arguments are missing.
7.161
7.162 @@ -1473,6 +1506,13 @@
7.163 self.context_index += 1
7.164 self.max_context_index = max(self.context_index, self.max_context_index)
7.165
7.166 + def next_accessor(self):
7.167 +
7.168 + "Allocate the next accessor value storage."
7.169 +
7.170 + self.accessor_index += 1
7.171 + self.max_accessor_index = max(self.accessor_index, self.max_accessor_index)
7.172 +
7.173 def always_callable(self, refs):
7.174
7.175 "Determine whether all 'refs' are callable."
7.176 @@ -1527,8 +1567,9 @@
7.177
7.178 name = self.get_lambda_name()
7.179 function_name = self.get_object_path(name)
7.180 -
7.181 - defaults = self.process_function_defaults(n, name, function_name, "__tmp_value")
7.182 + instance_name = "__get_accessor(%d)" % self.accessor_index
7.183 +
7.184 + defaults = self.process_function_defaults(n, name, function_name, instance_name)
7.185
7.186 # Without defaults, produce an attribute referring to the function.
7.187
7.188 @@ -1539,11 +1580,16 @@
7.189 # copy.
7.190
7.191 else:
7.192 - self.record_temp("__tmp_value")
7.193 - return make_expression("(__tmp_value = __ATTRVALUE(__COPY(&%s, sizeof(%s))), %s, __tmp_value)" % (
7.194 + self.record_temp("__tmp_values")
7.195 + return make_expression("""\
7.196 +(__set_accessor(%d, __ATTRVALUE(__COPY(&%s, sizeof(%s)))),
7.197 + %s,
7.198 + __get_accessor(%d))""" % (
7.199 + self.accessor_index,
7.200 encode_path(function_name),
7.201 encode_symbol("obj", function_name),
7.202 - ", ".join(defaults)))
7.203 + ", ".join(defaults),
7.204 + self.accessor_index))
7.205
7.206 def process_logical_node(self, n):
7.207
7.208 @@ -2119,24 +2165,21 @@
7.209
7.210 "Write temporary storage employed by 'name'."
7.211
7.212 - # Provide space for the given number of targets.
7.213 -
7.214 - targets = self.max_function_target
7.215 + # Provide space for the recorded number of temporary variables.
7.216
7.217 if self.uses_temp(name, "__tmp_targets"):
7.218 - self.writeline("__attr __tmp_targets[%d];" % targets)
7.219 -
7.220 - index = self.max_context_index
7.221 + self.writeline("__attr __tmp_targets[%d];" % self.max_function_target)
7.222
7.223 if self.uses_temp(name, "__tmp_contexts"):
7.224 - self.writeline("__attr __tmp_contexts[%d];" % index)
7.225 + self.writeline("__attr __tmp_contexts[%d];" % self.max_context_index)
7.226 +
7.227 + if self.uses_temp(name, "__tmp_values"):
7.228 + self.writeline("__attr __tmp_values[%d];" % self.max_accessor_index)
7.229
7.230 # Add temporary variable usage details.
7.231
7.232 if self.uses_temp(name, "__tmp_private_context"):
7.233 self.writeline("__attr __tmp_private_context;")
7.234 - if self.uses_temp(name, "__tmp_value"):
7.235 - self.writeline("__attr __tmp_value;")
7.236 if self.uses_temp(name, "__tmp_target_value"):
7.237 self.writeline("__attr __tmp_target_value;")
7.238 if self.uses_temp(name, "__tmp_result"):
8.1 --- a/transresults.py Thu Jul 12 18:19:02 2018 +0200
8.2 +++ b/transresults.py Sun Jul 22 00:19:35 2018 +0200
8.3 @@ -3,7 +3,7 @@
8.4 """
8.5 Translation result abstractions.
8.6
8.7 -Copyright (C) 2016, 2017 Paul Boddie <paul@boddie.org.uk>
8.8 +Copyright (C) 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
8.9
8.10 This program is free software; you can redistribute it and/or modify it under
8.11 the terms of the GNU General Public License as published by the Free Software
8.12 @@ -179,13 +179,15 @@
8.13 "A translation result for an attribute access."
8.14
8.15 def __init__(self, instructions, refs, location, context_identity,
8.16 - context_identity_verified, accessor_test):
8.17 + context_identity_verified, accessor_test, accessor_stored):
8.18 +
8.19 InstructionSequence.__init__(self, instructions)
8.20 self.refs = refs
8.21 self.location = location
8.22 self.context_identity = context_identity
8.23 self.context_identity_verified = context_identity_verified
8.24 self.accessor_test = accessor_test
8.25 + self.accessor_stored = accessor_stored
8.26
8.27 def references(self):
8.28 return self.refs
8.29 @@ -202,6 +204,9 @@
8.30 def tests_accessor(self):
8.31 return self.accessor_test
8.32
8.33 + def stores_accessor(self):
8.34 + return self.accessor_stored
8.35 +
8.36 def get_origin(self):
8.37 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
8.38
8.39 @@ -220,8 +225,10 @@
8.40 return encode_instructions(self.instructions)
8.41
8.42 def __repr__(self):
8.43 - return "AttrResult(%r, %r, %r, %r, %r)" % (self.instructions, self.refs,
8.44 - self.location, self.context_identity, self.accessor_test)
8.45 + return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % (
8.46 + self.instructions, self.refs, self.location,
8.47 + self.context_identity, self.context_identity_verified,
8.48 + self.accessor_test, self.accessor_stored)
8.49
8.50 class AliasResult(NameRef, Result):
8.51