Lichen

Changeset

964:e98699004465
2021-11-28 Paul Boddie raw files shortlog changelog graph Introduced support for values that can be allocated on a special thread-local stack, copied around and overwritten/mutated, demonstrating the concept using integer and floating-point numbers. Various complications arise with special attributes (such as __data__) due to the way references are tagged to indicate mutable values, and attribute slots must be cleared in locals, objects and fragments before values are stored since target slots are tested for mutable values. tagged-address-values
encoders.py (file) generator.py (file) templates/native/common.c (file) templates/native/iconv.c (file) templates/native/list.c (file) templates/ops.c (file) templates/ops.h (file) templates/progops.c (file) templates/progops.h (file) templates/types.h (file) tests/values.py (file) translator.py (file) transresults.py (file)
     1.1 --- a/encoders.py	Sun Nov 14 00:50:17 2021 +0100
     1.2 +++ b/encoders.py	Sun Nov 28 02:03:21 2021 +0100
     1.3 @@ -3,7 +3,7 @@
     1.4  """
     1.5  Encoder functions, producing representations of program objects.
     1.6  
     1.7 -Copyright (C) 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     1.8 +Copyright (C) 2016, 2017, 2018, 2021 Paul Boddie <paul@boddie.org.uk>
     1.9  
    1.10  This program is free software; you can redistribute it and/or modify it under
    1.11  the terms of the GNU General Public License as published by the Free Software
    1.12 @@ -286,6 +286,16 @@
    1.13      "<accessor>", "<context>", "<name>", "<private_context>", "<target_accessor>"
    1.14      )
    1.15  
    1.16 +# Instructions that need to be customised for certain attributes.
    1.17 +
    1.18 +via_object_ops = (
    1.19 +    "__store_via_object", "__check_and_store_via_object",
    1.20 +    )
    1.21 +
    1.22 +# NOTE: This duplicates a definition in transresults.
    1.23 +
    1.24 +special_attributes = ("__args__", "__data__", "__key__", "__size__")
    1.25 +
    1.26  def encode_access_instruction(instruction, subs, accessor_index, context_index):
    1.27  
    1.28      """
    1.29 @@ -379,6 +389,11 @@
    1.30      elif not args:
    1.31          op = "&%s" % encode_path(op)
    1.32  
    1.33 +    # Substitute the operation for certain attributes.
    1.34 +
    1.35 +    if op in via_object_ops and args[1] in special_attributes:
    1.36 +        op = "%s_internal" % op
    1.37 +
    1.38      return "%s%s" % (op, argstr), substituted
    1.39  
    1.40  def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index):
     2.1 --- a/generator.py	Sun Nov 14 00:50:17 2021 +0100
     2.2 +++ b/generator.py	Sun Nov 28 02:03:21 2021 +0100
     2.3 @@ -192,6 +192,7 @@
     2.4              print >>f_code, """\
     2.5  #include <string.h>
     2.6  #include <stdio.h>
     2.7 +#include <threads.h>
     2.8  #include "gc.h"
     2.9  #include "signals.h"
    2.10  #include "types.h"
    2.11 @@ -277,8 +278,9 @@
    2.12                  # Write a table for all objects.
    2.13  
    2.14                  table = []
    2.15 -                self.populate_table(Reference(kind, path), table)
    2.16 -                self.write_table(f_decls, f_defs, table_name, structure_size, table)
    2.17 +                self.populate_table(ref, table)
    2.18 +                self.write_table(f_decls, f_defs, table_name, structure_size,
    2.19 +                                 table, ref)
    2.20  
    2.21              # Generate function instances.
    2.22  
    2.23 @@ -320,6 +322,9 @@
    2.24                  # Signature: __attr <name>(...);
    2.25  
    2.26                  parameters = self.importer.function_parameters[path]
    2.27 +
    2.28 +                # Include the context plus the original parameters.
    2.29 +
    2.30                  l = ["__attr"] * (len(parameters) + 1)
    2.31                  print >>f_signatures, "__attr %s(%s);" % (encode_function_pointer(path), ", ".join(l))
    2.32  
    2.33 @@ -766,26 +771,41 @@
    2.34                  f_consts.write("    %s = %d" % (pos_encoder(attrname), i))
    2.35          print >>f_consts, "\n    };"
    2.36  
    2.37 -    def write_table(self, f_decls, f_defs, table_name, structure_size, table):
    2.38 +    def write_table(self, f_decls, f_defs, table_name, structure_size, table,
    2.39 +                    ref):
    2.40  
    2.41          """
    2.42          Write the declarations to 'f_decls' and definitions to 'f_defs' for
    2.43          the object having the given 'table_name' and the given 'structure_size',
    2.44          with 'table' details used to populate the definition.
    2.45 +
    2.46 +        To support value copying, the 'ref' is provided to be able to encode the
    2.47 +        structure size.
    2.48          """
    2.49  
    2.50          print >>f_decls, "extern const __table %s;\n" % table_name
    2.51  
    2.52 +        # Generate an object size of 0 for non-copyable types.
    2.53 +
    2.54 +        path = ref.get_origin()
    2.55 +
    2.56 +        if ref.get_kind() == "<instance>" and self.trailing_data_types.has_key(path):
    2.57 +            obj_type = encode_symbol("obj", path) or "__obj"
    2.58 +            obj_size = "sizeof(%s)" % obj_type
    2.59 +        else:
    2.60 +            obj_size = "0"
    2.61 +
    2.62          # Write the corresponding definition.
    2.63  
    2.64          print >>f_defs, """\
    2.65  const __table %s = {
    2.66      %s,
    2.67 +    %s,
    2.68      {
    2.69          %s
    2.70      }
    2.71  };
    2.72 -""" % (table_name, structure_size,
    2.73 +""" % (table_name, structure_size, obj_size,
    2.74         ",\n        ".join(table))
    2.75  
    2.76      def write_parameter_table(self, f_decls, f_defs, table_name, min_parameters,
    2.77 @@ -1137,13 +1157,24 @@
    2.78          the 'attrs' mapping, adding entries to the 'trailing' member collection.
    2.79          """
    2.80  
    2.81 +        if self.get_trailing_data_type(ref):
    2.82 +            trailing.append(encode_literal_constant_value(attrs["__trailing__"]))
    2.83 +
    2.84 +    def get_trailing_data_type(self, ref):
    2.85 +
    2.86 +        """
    2.87 +        For the structure having the given 'ref', return the type of any
    2.88 +        trailing data.
    2.89 +        """
    2.90 +
    2.91          structure_ref = self.get_target_structure(ref)
    2.92  
    2.93          # Instances with trailing data.
    2.94  
    2.95 -        if structure_ref.get_kind() == "<instance>" and \
    2.96 -           structure_ref.get_origin() in self.trailing_data_types:
    2.97 -            trailing.append(encode_literal_constant_value(attrs["__trailing__"]))
    2.98 +        if structure_ref.get_kind() == "<instance>":
    2.99 +            return self.trailing_data_types.get(structure_ref.get_origin())
   2.100 +        else:
   2.101 +            return None
   2.102  
   2.103      def get_target_structure(self, ref):
   2.104  
   2.105 @@ -1300,28 +1331,44 @@
   2.106          """
   2.107  
   2.108          print >>f_code, """\
   2.109 +_Thread_local __attr __stack;
   2.110 +
   2.111 +/* Global used to prevent compiler over-optimisation. */
   2.112 +
   2.113 +__attr __stack_global;
   2.114 +
   2.115  int main(int argc, char *argv[])
   2.116  {
   2.117      __exc __tmp_exc;
   2.118 +    __attr __stack_base;
   2.119  
   2.120      GC_INIT();
   2.121  
   2.122      __signals_install_handlers();
   2.123 +    __stack = __stack_init();
   2.124 +
   2.125 +    /* Record the stack accessor on the function stack for libgc. */
   2.126 +
   2.127 +    __stack_base = __stack;
   2.128 +    __stack_global = __stack_base;
   2.129  
   2.130      __Try
   2.131      {"""
   2.132  
   2.133 +        # Write a main function invocation for all but the native modules.
   2.134 +
   2.135          for name in self.importer.order_modules():
   2.136 +            parts = name.split(".")
   2.137 +
   2.138 +            if parts[0] == "native":
   2.139 +                continue
   2.140 +
   2.141              function_name = "__main_%s" % encode_path(name)
   2.142              print >>f_signatures, "void %s();" % function_name
   2.143 -
   2.144 -            # Omit the native modules.
   2.145 +            print >>f_code, """\
   2.146 +        %s();""" % function_name
   2.147  
   2.148 -            parts = name.split(".")
   2.149 -
   2.150 -            if parts[0] != "native":
   2.151 -                print >>f_code, """\
   2.152 -        %s();""" % function_name
   2.153 +        # Finish the main section with an exception handler.
   2.154  
   2.155          print >>f_code, """\
   2.156      }
     3.1 --- a/templates/native/common.c	Sun Nov 14 00:50:17 2021 +0100
     3.2 +++ b/templates/native/common.c	Sun Nov 28 02:03:21 2021 +0100
     3.3 @@ -29,7 +29,7 @@
     3.4  __attr __new_int(__int n)
     3.5  {
     3.6      /* Create a new int and set the trailing data. */
     3.7 -    __attr attr = __NEWINSTANCEIM(__builtins___int_int);
     3.8 +    __attr attr = __NEWINSTANCE_STACK(__builtins___int_int);
     3.9      __set_trailing_data(attr, __builtins___int_int, n);
    3.10      return __TO_MUTABLE(attr);
    3.11  }
    3.12 @@ -38,9 +38,9 @@
    3.13  {
    3.14      /* Create a new string and mutate the __data__, __size__ and __key__ attributes. */
    3.15      __attr attr = __NEWINSTANCE(__builtins___str_str);
    3.16 -    __store_via_object(__VALUE(attr), __data__, (__attr) {.strvalue=s});
    3.17 -    __store_via_object(__VALUE(attr), __size__, (__attr) {.sizevalue=size});
    3.18 -    __store_via_object(__VALUE(attr), __key__, __NULL);
    3.19 +    __store_via_object_internal(__VALUE(attr), __data__, (__attr) {.strvalue=s});
    3.20 +    __store_via_object_internal(__VALUE(attr), __size__, (__attr) {.sizevalue=size});
    3.21 +    __store_via_object_internal(__VALUE(attr), __key__, __NULL);
    3.22      return attr;
    3.23  }
    3.24  
    3.25 @@ -48,14 +48,14 @@
    3.26  {
    3.27      /* Create a new list and mutate the __data__ attribute. */
    3.28      __attr attr = __NEWINSTANCE(__builtins___list_list);
    3.29 -    __store_via_object(__VALUE(attr), __data__, (__attr) {.seqvalue=f});
    3.30 +    __store_via_object_internal(__VALUE(attr), __data__, (__attr) {.seqvalue=f});
    3.31      return attr;
    3.32  }
    3.33  
    3.34  __attr __new_float(__float n)
    3.35  {
    3.36      /* Create a new float and set the trailing data. */
    3.37 -    __attr attr = __NEWINSTANCEIM(__builtins___float_float);
    3.38 +    __attr attr = __NEWINSTANCE_STACK(__builtins___float_float);
    3.39      __set_trailing_data(attr, __builtins___float_float, n);
    3.40      return __TO_MUTABLE(attr);
    3.41  }
    3.42 @@ -70,13 +70,14 @@
    3.43      if (size >= capacity)
    3.44      {
    3.45          /* NOTE: Consider various restrictions on capacity increases. */
    3.46 -        n = capacity ? capacity * 2 : 1;
    3.47 +        n = capacity * 2 > size ? capacity * 2 : size + 1;
    3.48          newdata = (__fragment *) __REALLOCATE(data, __FRAGMENT_SIZE(n));
    3.49          newdata->capacity = n;
    3.50      }
    3.51  
    3.52      /* Insert the new element and increment the list size. */
    3.53 -    newdata->attrs[size] = value;
    3.54 +    newdata->attrs[size] = __RAWVALUE(0);
    3.55 +    __store_target(&newdata->attrs[size], value);
    3.56      newdata->size = size + 1;
    3.57  
    3.58      return newdata;
     4.1 --- a/templates/native/iconv.c	Sun Nov 14 00:50:17 2021 +0100
     4.2 +++ b/templates/native/iconv.c	Sun Nov 28 02:03:21 2021 +0100
     4.3 @@ -87,8 +87,10 @@
     4.4  
     4.5          /* Mutate the state to indicate the next input buffer position. */
     4.6  
     4.7 -        f->attrs[1] = __new_int(start + remaining - inbytesleft);
     4.8 -        f->attrs[2] = __new_int(inbytesleft);
     4.9 +        f->attrs[1] = __RAWVALUE(0);
    4.10 +        f->attrs[2] = __RAWVALUE(0);
    4.11 +        __store_target(&f->attrs[1], __new_int(start + remaining - inbytesleft));
    4.12 +        __store_target(&f->attrs[2], __new_int(inbytesleft));
    4.13  
    4.14          /* Incomplete sequence: raise the string in an OSError instead. */
    4.15  
     5.1 --- a/templates/native/list.c	Sun Nov 14 00:50:17 2021 +0100
     5.2 +++ b/templates/native/list.c	Sun Nov 28 02:03:21 2021 +0100
     5.3 @@ -56,7 +56,7 @@
     5.4  
     5.5      /* Replace the __data__ attribute if appropriate. */
     5.6      if (newdata != data)
     5.7 -        __store_via_object(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata}));
     5.8 +        __store_via_object_internal(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata}));
     5.9      return __builtins___none_None;
    5.10  }
    5.11  
    5.12 @@ -79,12 +79,16 @@
    5.13  
    5.14      /* Copy the elements from the other list and increment the list size. */
    5.15      for (i = size, j = 0; j < other_size; i++, j++)
    5.16 -        newdata->attrs[i] = other_data->attrs[j];
    5.17 +    {
    5.18 +        newdata->attrs[i] = __RAWVALUE(0);
    5.19 +        __store_target(&newdata->attrs[i], other_data->attrs[j]);
    5.20 +    }
    5.21 +
    5.22      newdata->size = n;
    5.23  
    5.24      /* Replace the __data__ attribute if appropriate. */
    5.25      if (newdata != data)
    5.26 -        __store_via_object(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata}));
    5.27 +        __store_via_object_internal(__VALUE(self), __data__, ((__attr) {.seqvalue=newdata}));
    5.28      return __builtins___none_None;
    5.29  }
    5.30  
    5.31 @@ -120,7 +124,7 @@
    5.32      __int i = __TOINT(index);
    5.33  
    5.34      /* Set the element. */
    5.35 -    elements[i] = value;
    5.36 +    __store_target(&elements[i], value);
    5.37      return __builtins___none_None;
    5.38  }
    5.39  
     6.1 --- a/templates/ops.c	Sun Nov 14 00:50:17 2021 +0100
     6.2 +++ b/templates/ops.c	Sun Nov 28 02:03:21 2021 +0100
     6.3 @@ -37,6 +37,24 @@
     6.4      return (pos < obj->table->size) && (obj->table->attrs[pos] == code);
     6.5  }
     6.6  
     6.7 +/* Generic load wrapper employing temporary stack storage. */
     6.8 +
     6.9 +__attr __load(__attr value)
    6.10 +{
    6.11 +    /* Copy values where appropriate. */
    6.12 +
    6.13 +    if (__COPYABLE(value))
    6.14 +    {
    6.15 +        size_t value_size = __INSTANCE_SIZE(__VALUE(value));
    6.16 +        __attr target = __stack_allocate(__stack, value_size);
    6.17 +
    6.18 +        __COPY_TO(__VALUE(value), __VALUE(target), value_size);
    6.19 +        return __TO_MUTABLE(target);
    6.20 +    }
    6.21 +    else
    6.22 +        return value;
    6.23 +}
    6.24 +
    6.25  /* Direct access and manipulation of static objects. */
    6.26  
    6.27  __attr __load_static_ignore(__ref obj)
    6.28 @@ -76,8 +94,71 @@
    6.29  
    6.30  /* Direct storage operations. */
    6.31  
    6.32 +__attr __store_local(__attr target, __attr value)
    6.33 +{
    6.34 +    /* Copy values where appropriate. */
    6.35 +
    6.36 +    if (__COPYABLE(value))
    6.37 +    {
    6.38 +        size_t value_size = __INSTANCE_SIZE(__VALUE(value));
    6.39 +
    6.40 +        /* Allocate new space for values if the target is not a mutable value
    6.41 +           or refers to an object that is too small for the value. */
    6.42 +
    6.43 +        if ((__VALUE(target) == NULL) || !__MUTABLE(target) || (value_size > __INSTANCE_SIZE(__VALUE(target))))
    6.44 +            target = __stack_allocate(__stack, value_size);
    6.45 +
    6.46 +        __COPY_TO(__VALUE(value), __VALUE(target), value_size);
    6.47 +        return __TO_MUTABLE(target);
    6.48 +    }
    6.49 +    else
    6.50 +        return value;
    6.51 +}
    6.52 +
    6.53 +void __store_target(__attr *target, __attr value)
    6.54 +{
    6.55 +    /* Copy values where appropriate. */
    6.56 +
    6.57 +    if (__COPYABLE(value))
    6.58 +    {
    6.59 +        size_t value_size = __INSTANCE_SIZE(__VALUE(value));
    6.60 +
    6.61 +        if ((__VALUE(*target) == NULL) || (!__MUTABLE(*target)) || (value_size > __INSTANCE_SIZE(__VALUE(*target))))
    6.62 +            *target = __ATTRVALUE(__ALLOCATEIM(1, value_size));
    6.63 +
    6.64 +        __COPY_TO(__VALUE(value), __VALUE(*target), value_size);
    6.65 +        *target = __TO_MUTABLE(*target);
    6.66 +    }
    6.67 +    else
    6.68 +        *target = value;
    6.69 +}
    6.70 +
    6.71  int __store_via_object__(__ref obj, int pos, __attr value)
    6.72  {
    6.73 +    /* Copy values where appropriate. */
    6.74 +
    6.75 +    if (__COPYABLE(value))
    6.76 +    {
    6.77 +        size_t value_size = __INSTANCE_SIZE(__VALUE(value));
    6.78 +        __attr target = obj->attrs[pos];
    6.79 +
    6.80 +        /* Allocate new space for values if the target is not a mutable value
    6.81 +           or refers to an object that is too small for the value. */
    6.82 +
    6.83 +        if ((__VALUE(target) == NULL) || (!__MUTABLE(target)) || (value_size > __INSTANCE_SIZE(__VALUE(target))))
    6.84 +            target = __ATTRVALUE(__ALLOCATEIM(1, value_size));
    6.85 +
    6.86 +        __COPY_TO(__VALUE(value), __VALUE(target), value_size);
    6.87 +        obj->attrs[pos] = __TO_MUTABLE(target);
    6.88 +    }
    6.89 +    else
    6.90 +        obj->attrs[pos] = value;
    6.91 +
    6.92 +    return 1;
    6.93 +}
    6.94 +
    6.95 +int __store_via_object_internal__(__ref obj, int pos, __attr value)
    6.96 +{
    6.97      obj->attrs[pos] = value;
    6.98      return 1;
    6.99  }
   6.100 @@ -215,6 +296,20 @@
   6.101      return 0;
   6.102  }
   6.103  
   6.104 +int __check_and_store_via_object_internal__(__ref obj, int pos, int code, __attr value)
   6.105 +{
   6.106 +    if (__HASATTR(obj, pos, code))
   6.107 +    {
   6.108 +        __store_via_object_internal__(obj, pos, value);
   6.109 +        return 1;
   6.110 +    }
   6.111 +
   6.112 +    /* No suitable attribute. */
   6.113 +
   6.114 +    __raise_type_error();
   6.115 +    return 0;
   6.116 +}
   6.117 +
   6.118  int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value)
   6.119  {
   6.120      if (__check_and_store_via_object__(obj, pos, code, value))
   6.121 @@ -438,9 +533,95 @@
   6.122  
   6.123  /* Copying of structures. */
   6.124  
   6.125 -__ref __COPY(__ref obj, int size)
   6.126 +__ref __COPY(__ref obj, size_t size)
   6.127  {
   6.128      __ref copy = (__ref) __ALLOCATE(1, size);
   6.129      memcpy(copy, obj, size);
   6.130      return copy;
   6.131  }
   6.132 +
   6.133 +void __COPY_TO(__ref source, __ref target, size_t size)
   6.134 +{
   6.135 +    memcpy(target, source, size);
   6.136 +}
   6.137 +
   6.138 +/* Stack management. */
   6.139 +
   6.140 +__attr __stack_init()
   6.141 +{
   6.142 +    __attr __stack;
   6.143 +
   6.144 +    __stack.stackdesc = (__stackdesc *) __ALLOCATE(1, sizeof(__stackdesc));
   6.145 +    __stack.stackdesc->current = NULL;
   6.146 +    __stack_expand(__stack);
   6.147 +
   6.148 +    return __stack;
   6.149 +}
   6.150 +
   6.151 +__attr __stack_allocate(__attr __stack, size_t size)
   6.152 +{
   6.153 +    char *result;
   6.154 +    __section *section = __stack.stackdesc->current;
   6.155 +
   6.156 +    if (section->limit - section->level < size)
   6.157 +    {
   6.158 +        __stack_expand(__stack);
   6.159 +        section = __stack.stackdesc->current;
   6.160 +    }
   6.161 +
   6.162 +    result = section->level;
   6.163 +    section->level += size;
   6.164 +
   6.165 +    return __ATTRVALUE(result);
   6.166 +}
   6.167 +
   6.168 +void __stack_expand(__attr __stack)
   6.169 +{
   6.170 +    __section *current = __stack.stackdesc->current;
   6.171 +    __section *section = (__section *) __ALLOCATE(1, sizeof(__section));
   6.172 +    char *base = (char *) __ALLOCATEIM(1, __STACK_SECTION_SIZE);
   6.173 +
   6.174 +    section->base = base;
   6.175 +    section->level = base;
   6.176 +    section->limit = base + __STACK_SECTION_SIZE;
   6.177 +    section->previous = current;
   6.178 +
   6.179 +    __stack.stackdesc->current = section;
   6.180 +}
   6.181 +
   6.182 +__attr __return(__attr result, __section *section, char *level)
   6.183 +{
   6.184 +    __ref obj = __VALUE(result);
   6.185 +    char *target = level;
   6.186 +    size_t size;
   6.187 +
   6.188 +    if (__COPYABLE(result))
   6.189 +    {
   6.190 +        size = __INSTANCE_SIZE(obj);
   6.191 +
   6.192 +        /* Test for space in the section. */
   6.193 +
   6.194 +        if (size > section->limit - level)
   6.195 +        {
   6.196 +            __stack_expand(__stack);
   6.197 +            section = __stack.stackdesc->current;
   6.198 +            target = section->base;
   6.199 +        }
   6.200 +
   6.201 +        /* Copy into the stack and adjust the level. */
   6.202 +
   6.203 +        __COPY_TO(obj, (__ref) target, size);
   6.204 +        level = target + size;
   6.205 +
   6.206 +        /* Reference the new location of the object. */
   6.207 +
   6.208 +        result = __MUTABLEVALUE(target);
   6.209 +    }
   6.210 +
   6.211 +    /* Set the level and current section.*/
   6.212 +
   6.213 +    section->level = level;
   6.214 +    __stack.stackdesc->current = section;
   6.215 +
   6.216 +    return result;
   6.217 +}
     7.1 --- a/templates/ops.h	Sun Nov 14 00:50:17 2021 +0100
     7.2 +++ b/templates/ops.h	Sun Nov 28 02:03:21 2021 +0100
     7.3 @@ -44,12 +44,15 @@
     7.4  
     7.5  /* Direct storage operations. */
     7.6  
     7.7 +void __store_target(__attr *target, __attr value);
     7.8  int __store_via_class__(__ref obj, int pos, __attr value);
     7.9  int __store_via_object__(__ref obj, int pos, __attr value);
    7.10 +int __store_via_object_internal__(__ref obj, int pos, __attr value);
    7.11  int __get_class_and_store__(__ref obj, int pos, __attr value);
    7.12  
    7.13  #define __store_via_class(OBJ, ATTRNAME, VALUE) (__store_via_class__(OBJ, __ATTRPOS(ATTRNAME), VALUE))
    7.14  #define __store_via_object(OBJ, ATTRNAME, VALUE) (__store_via_object__(OBJ, __ATTRPOS(ATTRNAME), VALUE))
    7.15 +#define __store_via_object_internal(OBJ, ATTRNAME, VALUE) (__store_via_object_internal__(OBJ, __ATTRPOS(ATTRNAME), VALUE))
    7.16  #define __get_class_and_store(OBJ, ATTRNAME, VALUE) (__get_class_and_store__(OBJ, __ATTRPOS(ATTRNAME), VALUE))
    7.17  
    7.18  /* Introspection. */
    7.19 @@ -93,10 +96,12 @@
    7.20  
    7.21  int __check_and_store_via_class__(__ref obj, int pos, int code, __attr value);
    7.22  int __check_and_store_via_object__(__ref obj, int pos, int code, __attr value);
    7.23 +int __check_and_store_via_object_internal__(__ref obj, int pos, int code, __attr value);
    7.24  int __check_and_store_via_any__(__ref obj, int pos, int code, __attr value);
    7.25  
    7.26  #define __check_and_store_via_class(OBJ, ATTRNAME, VALUE) (__check_and_store_via_class__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE))
    7.27  #define __check_and_store_via_object(OBJ, ATTRNAME, VALUE) (__check_and_store_via_object__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE))
    7.28 +#define __check_and_store_via_object_internal(OBJ, ATTRNAME, VALUE) (__check_and_store_via_object_internal__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE))
    7.29  #define __check_and_store_via_any(OBJ, ATTRNAME, VALUE) (__check_and_store_via_any__(OBJ, __ATTRPOS(ATTRNAME), __ATTRCODE(ATTRNAME), VALUE))
    7.30  
    7.31  /* Context-related operations. */
    7.32 @@ -151,6 +156,24 @@
    7.33  
    7.34  /* Copying of structures. */
    7.35  
    7.36 -__ref __COPY(__ref obj, int size);
    7.37 +__ref __COPY(__ref obj, size_t size);
    7.38 +void __COPY_TO(__ref source, __ref target, size_t size);
    7.39 +
    7.40 +/* Stack management. */
    7.41 +
    7.42 +extern _Thread_local __attr __stack;
    7.43 +
    7.44 +#define __STACK_SECTION_SIZE 4096
    7.45 +
    7.46 +__attr __stack_init();
    7.47 +__attr __stack_allocate(__attr __stack, size_t size);
    7.48 +void __stack_expand(__attr __stack);
    7.49 +void __stack_contract(__attr __stack, char *level);
    7.50 +
    7.51 +/* Stack access. */
    7.52 +
    7.53 +__attr __load(__attr value);
    7.54 +__attr __store_local(__attr target, __attr value);
    7.55 +__attr __return(__attr result, __section *section, char *level);
    7.56  
    7.57  #endif /* __OPS_H__ */
     8.1 --- a/templates/progops.c	Sun Nov 14 00:50:17 2021 +0100
     8.2 +++ b/templates/progops.c	Sun Nov 28 02:03:21 2021 +0100
     8.3 @@ -42,6 +42,13 @@
     8.4      return __ATTRVALUE(obj);
     8.5  }
     8.6  
     8.7 +__attr __new_stack(const __table * table, __ref cls, size_t size)
     8.8 +{
     8.9 +    __attr attr = __stack_allocate(__stack, size);
    8.10 +    __init(__VALUE(attr), table, cls);
    8.11 +    return attr;
    8.12 +}
    8.13 +
    8.14  __attr __new_wrapper(__attr context, __attr attr)
    8.15  {
    8.16      return __new___builtins___core_wrapper(__NULL, context, attr);
    8.17 @@ -69,7 +76,10 @@
    8.18      /* Copy the given number of values. */
    8.19  
    8.20      for (i = 0; i < number; i++)
    8.21 -        data->attrs[i] = args[i];
    8.22 +    {
    8.23 +        data->attrs[i] = __RAWVALUE(0);
    8.24 +        __store_target(&data->attrs[i], args[i]);
    8.25 +    }
    8.26  
    8.27      data->size = number;
    8.28  }
    8.29 @@ -81,7 +91,7 @@
    8.30  
    8.31      /* Store a reference to the data in the object's __data__ attribute. */
    8.32  
    8.33 -    __store_via_object(__VALUE(self), __data__, (__attr) {.seqvalue=data});
    8.34 +    __store_via_object_internal(__VALUE(self), __data__, (__attr) {.seqvalue=data});
    8.35      __newdata_sequence(number, data, args);
    8.36      return self;
    8.37  }
    8.38 @@ -97,7 +107,7 @@
    8.39  
    8.40      /* Store a reference to the data in the object's __data__ attribute. */
    8.41  
    8.42 -    __store_via_object(__VALUE(self), __data__, (__attr) {.seqvalue=data});
    8.43 +    __store_via_object_internal(__VALUE(self), __data__, (__attr) {.seqvalue=data});
    8.44      __newdata_sequence(number, data, args);
    8.45      return self;
    8.46  }
    8.47 @@ -207,8 +217,10 @@
    8.48  /* Invoke the given callable, supplying keyword argument details in the given
    8.49     codes and arguments arrays, indicating the number of arguments described.
    8.50     The number of positional arguments is specified, and such arguments then
    8.51 -   follow as conventional function arguments. Typically, at least one argument
    8.52 -   is specified, starting with any context argument.
    8.53 +   follow as conventional function arguments.
    8.54 +
    8.55 +   Typically, at least one argument is specified, starting with any context
    8.56 +   argument.
    8.57  */
    8.58  
    8.59  __attr __invoke(__attr callable, int always_callable,
    8.60 @@ -220,9 +232,11 @@
    8.61      __attr target = __unwrap_callable(callable);
    8.62  
    8.63      /* Obtain the __args__ special member, referencing the parameter table. */
    8.64 +
    8.65 +    const __ptable *ptable = __check_and_load_via_object(__VALUE(target), __args__).ptable;
    8.66 +
    8.67      /* Refer to the table and minimum/maximum. */
    8.68  
    8.69 -    const __ptable *ptable = __check_and_load_via_object(__VALUE(target), __args__).ptable;
    8.70      const unsigned int min = ptable->min, max = ptable->max;
    8.71  
    8.72      /* Reserve enough space for the arguments. */
    8.73 @@ -270,10 +284,13 @@
    8.74              /* Check the table entry against the supplied argument details.
    8.75                 Set the argument but only if it does not overwrite positional
    8.76                 arguments. */
    8.77 -            /* NOTE: Should use a more specific exception. */
    8.78  
    8.79              if ((pos == -1) || (pos < nargs))
    8.80 +            {
    8.81 +                /* NOTE: Should use a more specific exception. */
    8.82 +
    8.83                  __raise_type_error();
    8.84 +            }
    8.85  
    8.86              /* Set the argument using the appropriate position. */
    8.87  
    8.88 @@ -290,7 +307,8 @@
    8.89      }
    8.90  
    8.91      /* Call with the prepared arguments via a special adaptor function that
    8.92 -       converts the array to an argument list. */
    8.93 +       converts the array to an argument list. The context argument occupies
    8.94 +       position #0. */
    8.95  
    8.96      return __call_with_args(
    8.97          always_callable ?
    8.98 @@ -336,7 +354,8 @@
    8.99  
   8.100      return value == (__ref) &__predefined___builtins___boolean_True ? 1 :
   8.101             value == (__ref) &__predefined___builtins___boolean_False ? 0 :
   8.102 -           __VALUE(__fn___builtins___boolean_bool(__NULL, attr)) == (__ref) &__predefined___builtins___boolean_True;
   8.103 +           __VALUE(__fn___builtins___boolean_bool(__NULL, attr)) ==
   8.104 +               (__ref) &__predefined___builtins___boolean_True;
   8.105  }
   8.106  
   8.107  /* Conversion of trailing data to an integer. */
     9.1 --- a/templates/progops.h	Sun Nov 14 00:50:17 2021 +0100
     9.2 +++ b/templates/progops.h	Sun Nov 28 02:03:21 2021 +0100
     9.3 @@ -26,6 +26,7 @@
     9.4  /* Generic instantiation operations, defining common members. */
     9.5  
     9.6  __attr __new(const __table *table, __ref cls, size_t size, int immutable);
     9.7 +__attr __new_stack(const __table *table, __ref cls, size_t size);
     9.8  __attr __new_wrapper(__attr context, __attr attr);
     9.9  
    9.10  /* Generic internal data allocation. */
    9.11 @@ -86,6 +87,7 @@
    9.12  #define __INSTANCETABLE(CLS) (__InstanceTable_##CLS)
    9.13  #define __NEWINSTANCE(CLS) __new(&__INSTANCETABLE(CLS), &CLS, __INSTANCESIZE(CLS), 0)
    9.14  #define __NEWINSTANCEIM(CLS) __new(&__INSTANCETABLE(CLS), &CLS, __INSTANCESIZE(CLS), 1)
    9.15 +#define __NEWINSTANCE_STACK(CLS) __new_stack(&__INSTANCETABLE(CLS), &CLS, __INSTANCESIZE(CLS))
    9.16  #define __ISINSTANCE(ATTR, TYPE) __BOOL(__fn_native_introspection_isinstance(__NULL, ATTR, TYPE))
    9.17  
    9.18  /* Operations for accessing trailing data. */
    10.1 --- a/templates/types.h	Sun Nov 14 00:50:17 2021 +0100
    10.2 +++ b/templates/types.h	Sun Nov 28 02:03:21 2021 +0100
    10.3 @@ -42,6 +42,7 @@
    10.4  typedef struct __table
    10.5  {
    10.6      const __pos size;
    10.7 +    const size_t obj_size;      /* size for value instance copying */
    10.8      const __code attrs[];
    10.9  } __table;
   10.10  
   10.11 @@ -80,6 +81,11 @@
   10.12  
   10.13  typedef _Float64 __float;
   10.14  
   10.15 +/* Introduce value stack section and descriptor. */
   10.16 +
   10.17 +typedef struct __section __section;
   10.18 +typedef struct __stackdesc __stackdesc;
   10.19 +
   10.20  /* Attribute value interpretations. */
   10.21  
   10.22  typedef union __attr
   10.23 @@ -101,6 +107,11 @@
   10.24      __fragment * seqvalue;      /* sequence data */
   10.25      void * datavalue;           /* object-specific data */
   10.26      __int sizevalue;            /* object-specific size */
   10.27 +
   10.28 +    /* Value stack parameter member. */
   10.29 +
   10.30 +    __stackdesc *stackdesc;     /* reference to value stack descriptor */
   10.31 +
   10.32  } __attr;
   10.33  
   10.34  typedef struct __obj
   10.35 @@ -115,7 +126,7 @@
   10.36  
   10.37  } __obj;
   10.38  
   10.39 -#define __INSTANCE_SIZE(NUMBER) ((NUMBER) * sizeof(__attr) + sizeof(__table *) + sizeof(__ppos))
   10.40 +#define __INSTANCE_SIZE(REF) ((REF)->table->obj_size)
   10.41  
   10.42  /* Fragments are simple collections of attributes employed by sequence types.
   10.43     They provide the basis of lists and tuples. */
   10.44 @@ -128,17 +139,33 @@
   10.45  
   10.46  #define __FRAGMENT_SIZE(NUMBER) ((NUMBER) * sizeof(__attr) + 2 * sizeof(__int))
   10.47  
   10.48 +/* Sections are used to provide the value stack. */
   10.49 +
   10.50 +typedef struct __section
   10.51 +{
   10.52 +    char *base, *level, *limit;
   10.53 +    __section *previous;
   10.54 +} __section;
   10.55 +
   10.56 +/* The value stack descriptor references the current value stack section. */
   10.57 +
   10.58 +typedef struct __stackdesc
   10.59 +{
   10.60 +    __section *current;
   10.61 +} __stackdesc;
   10.62 +
   10.63  /* Attribute interpretation. */
   10.64  
   10.65  #define __NUM_TAG_BITS      2
   10.66 -#define __TAG_COPYABLE      0b01UL
   10.67  #define __TAG_MUTABLE       0b10UL
   10.68  #define __TAG_MASK          0b11UL
   10.69  
   10.70 -#define __COPYABLE(ATTR)    ((ATTR).rawvalue & __TAG_COPYABLE)
   10.71 +#define __COPYABLE(ATTR)    (__VALUE(ATTR)->table->obj_size != 0)
   10.72  #define __MUTABLE(ATTR)     ((ATTR).rawvalue & __TAG_MUTABLE)
   10.73  #define __TO_IMMUTABLE(ATTR) ((__attr) {.rawvalue=(ATTR).rawvalue & (~__TAG_MUTABLE)})
   10.74 -#define __TO_MUTABLE(ATTR)   ((__attr) {.rawvalue=(ATTR).rawvalue | __TAG_MASK})
   10.75 +#define __TO_MUTABLE(ATTR)   ((__attr) {.rawvalue=(ATTR).rawvalue | __TAG_MUTABLE})
   10.76 +#define __MUTABLEVALUE(REF) ((__attr) {.rawvalue=(uintptr_t) REF | __TAG_MUTABLE})
   10.77 +#define __RAWVALUE(VALUE)   ((__attr) {.rawvalue=VALUE})
   10.78  
   10.79  /* Attribute value setting. */
   10.80  
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/tests/values.py	Sun Nov 28 02:03:21 2021 +0100
    11.3 @@ -0,0 +1,19 @@
    11.4 +class X:
    11.5 +    def __init__(self, a, b):
    11.6 +        self.a = a
    11.7 +        self.b = b
    11.8 +
    11.9 +def f(a, x):
   11.10 +    x.a = 3                 # x.b
   11.11 +    print a                 # 2
   11.12 +    print x.a               # 3
   11.13 +    return a + x.a
   11.14 +
   11.15 +def g(x):
   11.16 +    return x.a, x.b
   11.17 +
   11.18 +x = X(2, 3)
   11.19 +y = f(x.a, x)
   11.20 +print y                     # 5
   11.21 +z = g(x)
   11.22 +print z                     # (3, 3)
    12.1 --- a/translator.py	Sun Nov 14 00:50:17 2021 +0100
    12.2 +++ b/translator.py	Sun Nov 28 02:03:21 2021 +0100
    12.3 @@ -3,7 +3,7 @@
    12.4  """
    12.5  Translate programs.
    12.6  
    12.7 -Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
    12.8 +Copyright (C) 2015-2018, 2021 Paul Boddie <paul@boddie.org.uk>
    12.9  
   12.10  This program is free software; you can redistribute it and/or modify it under
   12.11  the terms of the GNU General Public License as published by the Free Software
   12.12 @@ -37,7 +37,7 @@
   12.13                           AliasResult, AttrResult, Expression, InstantiationResult, \
   12.14                           InvocationResult, LogicalOperationResult, \
   12.15                           LogicalResult, NegationResult, PredefinedConstantRef, \
   12.16 -                         ReturnRef
   12.17 +                         ReturnRef, special_attributes
   12.18  from StringIO import StringIO
   12.19  import compiler
   12.20  import sys
   12.21 @@ -619,7 +619,8 @@
   12.22          del self.attrs[0]
   12.23          return AttrResult(output, refs, location,
   12.24                            context_identity, context_identity_verified,
   12.25 -                          accessor_test, accessor_stored)
   12.26 +                          accessor_test, accessor_stored, n.attrname,
   12.27 +                          self.in_assignment)
   12.28  
   12.29      def init_substitutions(self):
   12.30  
   12.31 @@ -806,9 +807,23 @@
   12.32              if ref and not ref.static():
   12.33                  parent, attrname = path.rsplit(".", 1)
   12.34  
   12.35 -                self.writestmt("__store_via_object(&%s, %s, __load_via_object(&%s, %s));" % (
   12.36 -                    encode_path(class_name), name,
   12.37 -                    encode_path(parent), attrname
   12.38 +                # NOTE: This is a superficial test for internal attributes that
   12.39 +                # NOTE: relies on such attributes being used directly and passed
   12.40 +                # NOTE: to native code.
   12.41 +
   12.42 +                if name in special_attributes:
   12.43 +                    store_op = "__store_via_object_internal"
   12.44 +                else:
   12.45 +                    store_op = "__store_via_object"
   12.46 +
   12.47 +                if attrname in special_attributes:
   12.48 +                    load_op = "__load_via_object_internal"
   12.49 +                else:
   12.50 +                    load_op = "__load_via_object"
   12.51 +
   12.52 +                self.writestmt("%s(&%s, %s, %s(&%s, %s));" % (
   12.53 +                    store_op, encode_path(class_name), name,
   12.54 +                    load_op, encode_path(parent), attrname
   12.55                      ))
   12.56  
   12.57      def process_from_node(self, n):
   12.58 @@ -883,6 +898,11 @@
   12.59                      compiler.ast.AssAttr(compiler.ast.Name("self"), name, "OP_ASSIGN"),
   12.60                      compiler.ast.Name(name))
   12.61  
   12.62 +        # Record the value stack level.
   12.63 +
   12.64 +        self.writestmt("__section *__stack_section = __stack.stackdesc->current;")
   12.65 +        self.writestmt("char *__stack_level = __stack_section->level; (void) __stack_level;")
   12.66 +
   12.67          # Produce the body and any additional return statement.
   12.68  
   12.69          expr = self.process_structure_node(n.code) or \
   12.70 @@ -1281,8 +1301,8 @@
   12.71              self.record_temp("__tmp_values")
   12.72  
   12.73          # Arguments are presented in a temporary frame array with any context
   12.74 -        # always being the first argument. Where it would be unused, it may be
   12.75 -        # set to null.
   12.76 +        # always being the first argument. Where the context would be unused, it
   12.77 +        # may be set to null.
   12.78  
   12.79          if context_required:
   12.80              if have_access_context:
   12.81 @@ -1292,6 +1312,8 @@
   12.82          else:
   12.83              context_arg = "__NULL"
   12.84  
   12.85 +        # Introduce any context.
   12.86 +
   12.87          args = [context_arg]
   12.88          reserved_args = 1
   12.89  
   12.90 @@ -1324,12 +1346,9 @@
   12.91          for i, arg in enumerate(n.args):
   12.92              argexpr = self.process_structure_node(arg)
   12.93  
   12.94 -            # Obtain an appropriate argument representation. This prevents
   12.95 -            # copyable values from being mutable, but care must be taken to
   12.96 -            # prevent special internal attribute values represented using
   12.97 -            # attributes from being modified.
   12.98 -
   12.99 -            argrepr = argexpr.as_arg()
  12.100 +            # Obtain an appropriate argument representation.
  12.101 +
  12.102 +            argrepr = str(argexpr)
  12.103  
  12.104              # Store a keyword argument, either in the argument list or
  12.105              # in a separate keyword argument list for subsequent lookup.
  12.106 @@ -1758,7 +1777,7 @@
  12.107          if self.in_try_finally or self.in_try_except:
  12.108              self.writestmt("__Return(%s);" % expr)
  12.109          else:
  12.110 -            self.writestmt("return %s;" % expr)
  12.111 +            self.writestmt("return __return(%s, __stack_section, __stack_level);" % expr)
  12.112  
  12.113          return ReturnRef()
  12.114  
  12.115 @@ -2131,11 +2150,13 @@
  12.116  
  12.117          if names:
  12.118              names.sort()
  12.119 -            self.writeline("__attr %s;" % ", ".join(names))
  12.120 +            for n in names:
  12.121 +                self.writeline("__attr %s = (__attr) {.rawvalue = 0};" % n)
  12.122  
  12.123          if volatile_names:
  12.124              volatile_names.sort()
  12.125 -            self.writeline("volatile __attr %s;" % ", ".join(volatile_names))
  12.126 +            for n in volatile_names:
  12.127 +                self.writeline("volatile __attr %s = (__attr) {.rawvalue = 0};" % n)
  12.128  
  12.129          self.flush_unit(name, out)
  12.130  
    13.1 --- a/transresults.py	Sun Nov 14 00:50:17 2021 +0100
    13.2 +++ b/transresults.py	Sun Nov 28 02:03:21 2021 +0100
    13.3 @@ -24,6 +24,8 @@
    13.4  from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, NameRef, \
    13.5                      ResolvedNameRef, Result
    13.6  
    13.7 +special_attributes = ("__args__", "__data__", "__key__", "__size__")
    13.8 +
    13.9  # Classes representing intermediate translation results.
   13.10  
   13.11  class ReturnRef:
   13.12 @@ -59,12 +61,6 @@
   13.13      def __repr__(self):
   13.14          return "Expression(%r)" % self.s
   13.15  
   13.16 -    def as_arg(self):
   13.17 -
   13.18 -        "Return the expression without any mutable tag."
   13.19 -
   13.20 -        return self.s
   13.21 -
   13.22  class TrResolvedNameRef(ResolvedNameRef):
   13.23  
   13.24      "A reference to a name in the translation."
   13.25 @@ -114,54 +110,58 @@
   13.26              # Qualified names must be converted into parent-relative assignments.
   13.27  
   13.28              elif self.parent:
   13.29 -                return "__store_via_object(&%s, %s, %s)" % (
   13.30 -                    encode_path(self.parent), self.attrname, self.expr)
   13.31 +
   13.32 +                # NOTE: This is a superficial test for internal attributes that
   13.33 +                # NOTE: relies on such attributes being used directly and passed
   13.34 +                # NOTE: to native code.
   13.35 +
   13.36 +                if self.attrname in special_attributes:
   13.37 +                    op = "__store_via_object_internal"
   13.38 +                else:
   13.39 +                    op = "__store_via_object"
   13.40 +
   13.41 +                return "%s(&%s, %s, %s)" % (
   13.42 +                    op, encode_path(self.parent), self.attrname, self.expr)
   13.43  
   13.44              # All other assignments involve the names as they were given.
   13.45  
   13.46              else:
   13.47 -                return "%s = %s" % (self.attrname, self.expr)
   13.48 +                return "%s = __store_local(%s, %s)" % (self.attrname, self.attrname, self.expr)
   13.49  
   13.50          # Expressions.
   13.51  
   13.52 -        elif self.static_name:
   13.53 -            return "__ATTRVALUE(&%s)" % self.static_name
   13.54 +        if self.static_name:
   13.55 +            s = "__ATTRVALUE(&%s)" % self.static_name
   13.56  
   13.57          # Qualified names must be converted into parent-relative accesses.
   13.58  
   13.59          elif self.parent:
   13.60 -            return "__load_via_object(&%s, %s)" % (
   13.61 -                encode_path(self.parent), self.attrname)
   13.62 +
   13.63 +            # NOTE: This is a superficial test for internal attributes that
   13.64 +            # NOTE: relies on such attributes being used directly and passed
   13.65 +            # NOTE: to native code.
   13.66 +
   13.67 +            if self.attrname in special_attributes:
   13.68 +                op = "__load_via_object_internal"
   13.69 +            else:
   13.70 +                op = "__load_via_object"
   13.71 +
   13.72 +            s = "%s(&%s, %s)" % (
   13.73 +                op, encode_path(self.parent), self.attrname)
   13.74  
   13.75          # All other accesses involve the names as they were given.
   13.76  
   13.77          else:
   13.78 -            return "(%s)" % self.attrname
   13.79 -
   13.80 -    def as_arg(self):
   13.81 -
   13.82 -        "Return the expression without any mutable tag."
   13.83 -
   13.84 -        s = self.__str__()
   13.85 +            s = "(%s)" % self.attrname
   13.86  
   13.87 -        # NOTE: This is a superficial test for internal attributes that relies
   13.88 -        # NOTE: on such attributes being used directly and passed to native
   13.89 -        # NOTE: code.
   13.90 -
   13.91 -        if self.attrname in ("__data__", "__size__"):
   13.92 -            return s
   13.93 -        else:
   13.94 -            return "__TO_IMMUTABLE(%s)" % s
   13.95 +        return "__load(%s)" % s
   13.96  
   13.97  class TrConstantValueRef(ConstantValueRef):
   13.98  
   13.99      "A constant value reference in the translation."
  13.100  
  13.101      def __str__(self):
  13.102 -        return encode_literal_constant(self.number)
  13.103 -
  13.104 -    def as_arg(self):
  13.105 -        return self.__str__()
  13.106 +        return "__load(%s)" % encode_literal_constant(self.number)
  13.107  
  13.108  class TrLiteralSequenceRef(LiteralSequenceRef):
  13.109  
  13.110 @@ -170,9 +170,6 @@
  13.111      def __str__(self):
  13.112          return str(self.node)
  13.113  
  13.114 -    def as_arg(self):
  13.115 -        return self.__str__()
  13.116 -
  13.117  class TrInstanceRef(InstanceRef):
  13.118  
  13.119      "A reference representing instantiation of a class."
  13.120 @@ -193,15 +190,13 @@
  13.121      def __repr__(self):
  13.122          return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr)
  13.123  
  13.124 -    def as_arg(self):
  13.125 -        return self.__str__()
  13.126 -
  13.127  class AttrResult(Result, InstructionSequence):
  13.128  
  13.129      "A translation result for an attribute access."
  13.130  
  13.131      def __init__(self, instructions, refs, location, context_identity,
  13.132 -                 context_identity_verified, accessor_test, accessor_stored):
  13.133 +                 context_identity_verified, accessor_test, accessor_stored,
  13.134 +                 attrname, assignment):
  13.135  
  13.136          InstructionSequence.__init__(self, instructions)
  13.137          self.refs = refs
  13.138 @@ -210,6 +205,8 @@
  13.139          self.context_identity_verified = context_identity_verified
  13.140          self.accessor_test = accessor_test
  13.141          self.accessor_stored = accessor_stored
  13.142 +        self.attrname = attrname
  13.143 +        self.assignment = assignment
  13.144  
  13.145      def references(self):
  13.146          return self.refs
  13.147 @@ -244,16 +241,23 @@
  13.148          return bool(self.instructions)
  13.149  
  13.150      def __str__(self):
  13.151 -        return encode_instructions(self.instructions)
  13.152 +        s = encode_instructions(self.instructions)
  13.153 +
  13.154 +        # NOTE: This includes a superficial test for internal attributes that
  13.155 +        # NOTE: relies on such attributes being used directly and passedto
  13.156 +        # NOTE: native code.
  13.157 +
  13.158 +        if self.assignment or self.accessor_test or self.attrname in special_attributes:
  13.159 +            return s
  13.160 +        else:
  13.161 +            return "__load(%s)" % s
  13.162  
  13.163      def __repr__(self):
  13.164 -        return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % (
  13.165 +        return "AttrResult(%r, %r, %r, %r, %r, %r, %r, %r, %r)" % (
  13.166                  self.instructions, self.refs, self.location,
  13.167                  self.context_identity, self.context_identity_verified,
  13.168 -                self.accessor_test, self.accessor_stored)
  13.169 -
  13.170 -    def as_arg(self):
  13.171 -        return self.__str__()
  13.172 +                self.accessor_test, self.accessor_stored, self.attrname,
  13.173 +                self.assignment)
  13.174  
  13.175  class AliasResult(NameRef, Result):
  13.176  
  13.177 @@ -308,9 +312,6 @@
  13.178      def __repr__(self):
  13.179          return "AliasResult(%r, %r)" % (self.name_ref, self.refs)
  13.180  
  13.181 -    def as_arg(self):
  13.182 -        return self.__str__()
  13.183 -
  13.184  class InvocationResult(Result, InstructionSequence):
  13.185  
  13.186      "A translation result for an invocation."
  13.187 @@ -321,9 +322,6 @@
  13.188      def __repr__(self):
  13.189          return "InvocationResult(%r)" % self.instructions
  13.190  
  13.191 -    def as_arg(self):
  13.192 -        return self.__str__()
  13.193 -
  13.194  class InstantiationResult(InvocationResult, TrInstanceRef):
  13.195  
  13.196      "An instantiation result acting like an invocation result."
  13.197 @@ -364,9 +362,6 @@
  13.198      def __repr__(self):
  13.199          return "PredefinedConstantRef(%r)" % self.value
  13.200  
  13.201 -    def as_arg(self):
  13.202 -        return self.__str__()
  13.203 -
  13.204  class LogicalResult(Result):
  13.205  
  13.206      "A logical expression result."
  13.207 @@ -412,9 +407,6 @@
  13.208      def __repr__(self):
  13.209          return "NegationResult(%r)" % self.expr
  13.210  
  13.211 -    def as_arg(self):
  13.212 -        return self.__str__()
  13.213 -
  13.214  class LogicalOperationResult(LogicalResult):
  13.215  
  13.216      "A logical operation result."
  13.217 @@ -487,7 +479,4 @@
  13.218      def __repr__(self):
  13.219          return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction)
  13.220  
  13.221 -    def as_arg(self):
  13.222 -        return self.__str__()
  13.223 -
  13.224  # vim: tabstop=4 expandtab shiftwidth=4