# HG changeset patch # User Paul Boddie # Date 1636327546 -3600 # Node ID b07f6f6ce357d973939ea397e33b68640a42c7f0 # Parent 28b43ef743aebfd642116eb128fee97150c8338f Introduced copyable and mutable flags in the tagged region of attribute values. Such flags are set when new integer and floating point values are created, but the mutable flag is cleared when such attributes are propagated between functions in order to prevent values being replaced upon assignment to parameter names, this being a concern when value copying is introduced. diff -r 28b43ef743ae -r b07f6f6ce357 templates/types.h --- a/templates/types.h Sun Nov 07 01:18:51 2021 +0100 +++ b/templates/types.h Mon Nov 08 00:25:46 2021 +0100 @@ -135,12 +135,15 @@ /* Attribute interpretation. */ -#define __NUM_TAG_BITS 1 -#define __TAG_MUTABLE 0b1 -#define __TAG_MASK 0b1 +#define __NUM_TAG_BITS 2 +#define __TAG_COPYABLE 0b01UL +#define __TAG_MUTABLE 0b10UL +#define __TAG_MASK 0b11UL -#define __MUTABLE(ATTR) (((ATTR).rawvalue & __TAG_MASK) == __TAG_MUTABLE) -#define __TO_MUTABLE(ATTR) ((__attr) (((ATTR).rawvalue & (~__TAG_MASK)) | __TAG_MUTABLE)) +#define __COPYABLE(ATTR) ((ATTR).rawvalue & __TAG_COPYABLE) +#define __MUTABLE(ATTR) ((ATTR).rawvalue & __TAG_MUTABLE) +#define __TO_IMMUTABLE(ATTR) ((__attr) {.rawvalue=(ATTR).rawvalue & (~__TAG_MUTABLE)}) +#define __TO_MUTABLE(ATTR) ((__attr) {.rawvalue=(ATTR).rawvalue | __TAG_MASK}) /* Attribute value setting. */ diff -r 28b43ef743ae -r b07f6f6ce357 translator.py --- a/translator.py Sun Nov 07 01:18:51 2021 +0100 +++ b/translator.py Mon Nov 08 00:25:46 2021 +0100 @@ -1326,6 +1326,13 @@ for i, arg in enumerate(n.args): argexpr = self.process_structure_node(arg) + # Obtain an appropriate argument representation. This prevents + # copyable values from being mutable, but care must be taken to + # prevent special internal attribute values represented using + # attributes from being modified. + + argrepr = argexpr.as_arg() + # Store a keyword argument, either in the argument list or # in a separate keyword argument list for subsequent lookup. @@ -1340,12 +1347,12 @@ except ValueError: raise TranslateError("Argument %s is not recognised." % arg.name, self.get_namespace_path(), n) - args[argnum + reserved_args] = str(argexpr) + args[argnum + reserved_args] = argrepr # Otherwise, store the details in a separate collection. else: - kwargs.append(str(argexpr)) + kwargs.append(argrepr) kwcodes.append("{%s, %s}" % ( encode_ppos(arg.name), encode_pcode(arg.name))) @@ -1354,7 +1361,7 @@ else: try: - args[i + reserved_args] = str(argexpr) + args[i + reserved_args] = argrepr except IndexError: raise TranslateError("Too many arguments specified.", self.get_namespace_path(), n) diff -r 28b43ef743ae -r b07f6f6ce357 transresults.py --- a/transresults.py Sun Nov 07 01:18:51 2021 +0100 +++ b/transresults.py Mon Nov 08 00:25:46 2021 +0100 @@ -59,6 +59,12 @@ def __repr__(self): return "Expression(%r)" % self.s + def as_arg(self): + + "Return the expression without any mutable tag." + + return "__TO_IMMUTABLE(%s)" % self.s + class TrResolvedNameRef(ResolvedNameRef): "A reference to a name in the translation." @@ -67,6 +73,20 @@ ResolvedNameRef.__init__(self, name, ref, expr, is_global) self.location = location + # For sources, any identified static origin will be constant and thus + # usable directly. For targets, no constant should be assigned and thus + # the alias (or any plain name) will be used. + + self.static_ref = self.static() + origin = self.static_ref and self.get_origin() + self.static_name = origin and encode_path(origin) + + # Determine whether a qualified name is involved. + + t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1) + self.parent = len(t) > 1 and t[0] or None + self.attrname = t[-1] and encode_path(t[-1]) + def access_location(self): return self.location @@ -82,55 +102,56 @@ else: return encode_path(self.name) - # For sources, any identified static origin will be constant and thus - # usable directly. For targets, no constant should be assigned and thus - # the alias (or any plain name) will be used. - - ref = self.static() - origin = ref and self.get_origin() - static_name = origin and encode_path(origin) - - # Determine whether a qualified name is involved. - - t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1) - parent = len(t) > 1 and t[0] or None - attrname = t[-1] and encode_path(t[-1]) - # Assignments. if self.expr: # Eliminate assignments between constants. - if ref and self.expr.static(): + if self.static_ref and self.expr.static(): return "" # Qualified names must be converted into parent-relative assignments. - elif parent: + elif self.parent: return "__store_via_object(&%s, %s, %s)" % ( - encode_path(parent), attrname, self.expr) + encode_path(self.parent), self.attrname, self.expr) # All other assignments involve the names as they were given. else: - return "%s = %s" % (attrname, self.expr) + return "%s = %s" % (self.attrname, self.expr) # Expressions. - elif static_name: - return "__ATTRVALUE(&%s)" % static_name + elif self.static_name: + return "__ATTRVALUE(&%s)" % self.static_name # Qualified names must be converted into parent-relative accesses. - elif parent: + elif self.parent: return "__load_via_object(&%s, %s)" % ( - encode_path(parent), attrname) + encode_path(self.parent), self.attrname) # All other accesses involve the names as they were given. else: - return "(%s)" % attrname + return "(%s)" % self.attrname + + def as_arg(self): + + "Return the expression without any mutable tag." + + s = self.__str__() + + # NOTE: This is a superficial test for internal attributes that relies + # NOTE: on such attributes being used directly and passed to native + # NOTE: code. + + if self.attrname in ("__data__", "__size__"): + return s + else: + return "__TO_IMMUTABLE(%s)" % s class TrConstantValueRef(ConstantValueRef): @@ -139,6 +160,9 @@ def __str__(self): return encode_literal_constant(self.number) + def as_arg(self): + return self.__str__() + class TrLiteralSequenceRef(LiteralSequenceRef): "A reference representing a sequence of values." @@ -146,6 +170,9 @@ def __str__(self): return str(self.node) + def as_arg(self): + return self.__str__() + class TrInstanceRef(InstanceRef): "A reference representing instantiation of a class." @@ -166,6 +193,9 @@ def __repr__(self): return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr) + def as_arg(self): + return self.__str__() + class AttrResult(Result, InstructionSequence): "A translation result for an attribute access." @@ -222,6 +252,9 @@ self.context_identity, self.context_identity_verified, self.accessor_test, self.accessor_stored) + def as_arg(self): + return self.__str__() + class AliasResult(NameRef, Result): "An alias for other values." @@ -275,6 +308,9 @@ def __repr__(self): return "AliasResult(%r, %r)" % (self.name_ref, self.refs) + def as_arg(self): + return self.__str__() + class InvocationResult(Result, InstructionSequence): "A translation result for an invocation." @@ -285,6 +321,9 @@ def __repr__(self): return "InvocationResult(%r)" % self.instructions + def as_arg(self): + return self.__str__() + class InstantiationResult(InvocationResult, TrInstanceRef): "An instantiation result acting like an invocation result." @@ -325,6 +364,9 @@ def __repr__(self): return "PredefinedConstantRef(%r)" % self.value + def as_arg(self): + return self.__str__() + class LogicalResult(Result): "A logical expression result." @@ -370,6 +412,9 @@ def __repr__(self): return "NegationResult(%r)" % self.expr + def as_arg(self): + return self.__str__() + class LogicalOperationResult(LogicalResult): "A logical operation result." @@ -442,4 +487,7 @@ def __repr__(self): return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) + def as_arg(self): + return self.__str__() + # vim: tabstop=4 expandtab shiftwidth=4