class ValueTraceBase(object): # We are going to have many instance attributes __slots__ = ("owner", "usage_count", "has_potential_usages", "name_usages", "closure_usages", "is_escaped", "previous") @InstanceCounters.counted_init def __init__(self, owner, previous): self.owner = owner # Definite usage indicator. self.usage_count = 0 # Potential usages indicator that an assignment value may be used. self.has_potential_usages = False # If 0, this indicates, the variable name needs to be assigned as name. self.name_usages = 0 self.closure_usages = False # If False, this indicates that the value is not yet escaped. self.is_escaped = False # Previous trace this is replacing. self.previous = previous __del__ = InstanceCounters.counted_del() def getOwner(self): return self.owner def addClosureUsage(self): self.addUsage() self.closure_usages = True def addUsage(self): self.usage_count += 1 def addPotentialUsage(self): self.has_potential_usages = True def addNameUsage(self): self.usage_count += 1 self.name_usages += 1 def onValueEscape(self): self.is_escaped = True def isEscaped(self): return self.is_escaped def hasDefiniteUsages(self): return self.usage_count > 0 def getDefiniteUsages(self): return self.usage_count def hasPotentialUsages(self): return self.has_potential_usages def getNameUsageCount(self): return self.name_usages def getPrevious(self): return self.previous @staticmethod def isAssignTrace(): return False @staticmethod def isUninitTrace(): return False @staticmethod def isInitTrace(): return False @staticmethod def isUnknownTrace(): return False @staticmethod def isMergeTrace(): return False def mustHaveValue(self): # Merge traces have this overloaded. return self.isInitTrace() or self.isAssignTrace() @staticmethod def mustNotHaveValue(): return False def getReplacementNode(self, usage): # Virtual method, pylint: disable=no-self-use,unused-argument return None def hasShapeDictionaryExact(self): # Virtual method, pylint: disable=no-self-use return False
class ValueTraceBase(object): # We are going to have many instance attributes, but should strive to minimize, as # there is going to be a lot of fluctuation in these objects. __slots__ = ( "owner", "usage_count", "name_usage_count", "merge_usage_count", "closure_usages", "previous", ) @InstanceCounters.counted_init def __init__(self, owner, previous): self.owner = owner # Definite usage indicator. self.usage_count = 0 # If 0, this indicates, the variable name needs to be assigned as name. self.name_usage_count = 0 # If 0, this indicates no value merges happened on the value. self.merge_usage_count = 0 self.closure_usages = False # Previous trace this is replacing. self.previous = previous __del__ = InstanceCounters.counted_del() def __repr__(self): return "<%s of %s>" % (self.__class__.__name__, self.owner.getCodeName()) def getOwner(self): return self.owner @staticmethod def isLoopTrace(): return False def addUsage(self): self.usage_count += 1 def addNameUsage(self): self.usage_count += 1 self.name_usage_count += 1 if self.name_usage_count <= 2 and self.previous is not None: self.previous.addNameUsage() def addMergeUsage(self): self.usage_count += 1 self.merge_usage_count += 1 def getUsageCount(self): return self.usage_count def getNameUsageCount(self): return self.name_usage_count def getMergeUsageCount(self): return self.merge_usage_count def getMergeOrNameUsageCount(self): return self.merge_usage_count + self.name_usage_count def getPrevious(self): return self.previous @staticmethod def isAssignTrace(): return False @staticmethod def isUnassignedTrace(): return False @staticmethod def isDeletedTrace(): return False @staticmethod def isUninitTrace(): return False @staticmethod def isInitTrace(): return False @staticmethod def isUnknownTrace(): return False @staticmethod def isMergeTrace(): return False def mustHaveValue(self): """Will this definitely have a value. Every trace has this overloaded. """ assert False, self def mustNotHaveValue(self): """Will this definitely have a value. Every trace has this overloaded. """ assert False, self def getReplacementNode(self, usage): # Virtual method, pylint: disable=no-self-use,unused-argument return None def hasShapeDictionaryExact(self): # Virtual method, pylint: disable=no-self-use return False
class Variable(object): # We will need all of these attributes, since we track the global # state and cache some decisions as attributes, pylint: disable=too-many-instance-attributes __slots__ = ("variable_name", "owner", "version_number", "shared_users", "shared_scopes", "traces", "users", "writers") @InstanceCounters.counted_init def __init__(self, owner, variable_name): assert type(variable_name) is str, variable_name assert type(owner) not in (tuple, list), owner self.variable_name = variable_name self.owner = owner self.version_number = 0 self.shared_users = False self.shared_scopes = False self.traces = set() # Derived from all traces. self.users = None self.writers = None __del__ = InstanceCounters.counted_del() def getDescription(self): return "variable '%s'" % self.variable_name def getName(self): return self.variable_name def getOwner(self): return self.owner def getEntryPoint(self): return self.owner.getEntryPoint() def getCodeName(self): var_name = self.variable_name var_name = var_name.replace('.', '$') var_name = Utils.encodeNonAscii(var_name) return var_name def allocateTargetNumber(self): self.version_number += 1 return self.version_number # pylint: disable=no-self-use def isLocalVariable(self): return False def isParameterVariable(self): return False def isModuleVariable(self): return False def isTempVariable(self): return False # pylint: enable=R0201 def addVariableUser(self, user): # Update the shared scopes flag. if user is not self.owner: # These are not really scopes. self.shared_users = True if user.isExpressionGeneratorObjectBody() or \ user.isExpressionCoroutineObjectBody() or \ user.isExpressionAsyncgenObjectBody(): if self.owner is user.getParentVariableProvider(): return self.shared_scopes = True def isSharedAmongScopes(self): return self.shared_scopes def isSharedTechnically(self): if not self.shared_users: return False if not complete: return None if not self.users: return False owner = self.owner.getEntryPoint() for user in self.users: user = user.getEntryPoint() while user is not owner and \ ( (user.isExpressionFunctionBody() and not user.needsCreation()) or \ user.isExpressionClassBody() ): user = user.getParentVariableProvider() if user is not owner: return True return False def addTrace(self, variable_trace): self.traces.add(variable_trace) def removeTrace(self, variable_trace): # Make it unusable, and break GC cycles while at it. variable_trace.previous = None self.traces.remove(variable_trace) def updateUsageState(self): writers = set() users = set() for trace in self.traces: owner = trace.owner users.add(owner) if trace.isAssignTrace(): writers.add(owner) self.writers = writers self.users = users def hasWritesOutsideOf(self, user): if not complete: return None elif user in self.writers: return len(self.writers) > 1 else: return bool(self.writers) def hasAccessesOutsideOf(self, provider): if not complete: return None elif self.users is None: return False elif provider in self.users: return len(self.users) > 1 else: return bool(self.users) def hasDefiniteWrites(self): if not complete: return None else: return bool(self.writers) def getMatchingAssignTrace(self, assign_node): for trace in self.traces: if trace.isAssignTrace() and trace.getAssignNode() is assign_node: return trace return None def getTypeShapes(self): result = set() for trace in self.traces: if trace.isAssignTrace(): result.add( trace.getAssignNode().getAssignSource().getTypeShape()) elif trace.isUnknownTrace(): result.add(ShapeUnknown) elif trace.isUninitTrace(): if trace.hasDefiniteUsages() or trace.hasPotentialUsages(): result.add(ShapeUnknown) elif trace.isInitTrace(): result.add(ShapeUnknown) elif trace.isMergeTrace(): pass else: assert False, trace return result
class Variable(getMetaClassBase("Variable")): # We will need all of these attributes, since we track the global # state and cache some decisions as attributes. TODO: But in some # cases, part of the these might be moved to the outside. __slots__ = ( "variable_name", "owner", "version_number", "shared_users", "traces", "users", "writers", ) @InstanceCounters.counted_init def __init__(self, owner, variable_name): assert type(variable_name) is str, variable_name assert type(owner) not in (tuple, list), owner self.variable_name = variable_name self.owner = owner self.version_number = 0 self.shared_users = False self.traces = set() # Derived from all traces. self.users = None self.writers = None __del__ = InstanceCounters.counted_del() def finalize(self): del self.users del self.writers del self.traces del self.owner def __repr__(self): return "<%s '%s' of '%s'>" % ( self.__class__.__name__, self.variable_name, self.owner.getName(), ) @abstractmethod def getVariableType(self): pass def getDescription(self): return "variable '%s'" % self.variable_name def getName(self): return self.variable_name def getOwner(self): return self.owner def getEntryPoint(self): return self.owner.getEntryPoint() def getCodeName(self): var_name = self.variable_name var_name = var_name.replace(".", "$") var_name = Utils.encodeNonAscii(var_name) return var_name def allocateTargetNumber(self): self.version_number += 1 return self.version_number @staticmethod def isLocalVariable(): return False @staticmethod def isParameterVariable(): return False @staticmethod def isModuleVariable(): return False @staticmethod def isIncompleteModuleVariable(): return False @staticmethod def isTempVariable(): return False @staticmethod def isTempVariableBool(): return False @staticmethod def isLocalsDictVariable(): return False def addVariableUser(self, user): # Update the shared scopes flag. if user is not self.owner: self.shared_users = True # These are not really scopes, just shared uses. if (user.isExpressionGeneratorObjectBody() or user.isExpressionCoroutineObjectBody() or user.isExpressionAsyncgenObjectBody()): if self.owner is user.getParentVariableProvider(): return _variables_in_shared_scopes.add(self) def isSharedTechnically(self): if not self.shared_users: return False if not self.users: return False owner = self.owner.getEntryPoint() for user in self.users: user = user.getEntryPoint() while user is not owner and ( (user.isExpressionFunctionBody() and not user.needsCreation()) or user.isExpressionClassBody()): user = user.getParentVariableProvider() if user is not owner: return True return False def addTrace(self, variable_trace): self.traces.add(variable_trace) def removeTrace(self, variable_trace): self.traces.remove(variable_trace) def updateUsageState(self): writers = set() users = set() for trace in self.traces: owner = trace.owner users.add(owner) if trace.isAssignTrace(): writers.add(owner) elif trace.isDeletedTrace() and owner is not self.owner: writers.add(owner) self.writers = writers self.users = users def hasAccessesOutsideOf(self, provider): if not self.owner.locals_scope.complete: return None elif self.users is None: return False elif provider in self.users: return len(self.users) > 1 else: return bool(self.users) def getMatchingAssignTrace(self, assign_node): for trace in self.traces: if trace.isAssignTrace() and trace.getAssignNode() is assign_node: return trace return None def getMatchingDelTrace(self, del_node): for trace in self.traces: if trace.isDeletedTrace() and trace.getDelNode() is del_node: return trace return None def getTypeShapes(self): result = set() for trace in self.traces: if trace.isAssignTrace(): result.add(trace.getAssignNode().getTypeShape()) elif trace.isUnknownTrace(): result.add(tshape_unknown) elif trace.isInitTrace(): result.add(tshape_unknown) elif trace.isUnassignedTrace(): pass elif trace.isMergeTrace(): pass # TODO: Remove this and be not unknown. elif trace.isLoopTrace(): trace.getTypeShape().emitAlternatives(result.add) else: assert False, trace return result
class Variable: @InstanceCounters.counted_init def __init__(self, owner, variable_name): assert type(variable_name) is str, variable_name assert type(owner) not in (tuple, list), owner assert owner.getFullName self.variable_name = variable_name self.owner = owner self.read_only_indicator = None self.version_number = 0 __del__ = InstanceCounters.counted_del() def getName(self): return self.variable_name def getCodeName(self): var_name = self.variable_name var_name = var_name.replace('.', '$') var_name = Utils.encodeNonAscii(var_name) return var_name def getOwner(self): return self.owner def getReadOnlyIndicator(self): return self.read_only_indicator def setReadOnlyIndicator(self, value): assert value in (True, False) self.read_only_indicator = value def allocateTargetNumber(self): self.version_number += 1 return self.version_number # pylint: disable=R0201 def isLocalVariable(self): return False def isMaybeLocalVariable(self): return False def isParameterVariable(self): return False def isNestedParameterVariable(self): return False def isModuleVariable(self): return False def isTempVariable(self): return False # pylint: enable=R0201 def isSharedTechnically(self): from nuitka.VariableRegistry import isSharedTechnically return isSharedTechnically(self) def getDeclarationTypeCode(self): # Abstract method, pylint: disable=R0201 assert False
class VariableTraceBase: # We are going to have many instance attributes, pylint: disable=R0902 @InstanceCounters.counted_init def __init__(self, variable, version, previous): self.variable = variable self.version = version # Definite usage indicator. self.usage_count = 0 # Potential usages indicator that an assignment value may be used. self.has_potential_usages = False # If False, this indicates the trace has no explicit releases. self.has_releases = False # If False, this indicates, the variable name needs to be assigned. self.has_name_usages = False # If False, this indicates that the value is not yet escaped. self.is_escaped = False # Previous trace this is replacing. self.previous = previous __del__ = InstanceCounters.counted_del() def getVariable(self): return self.variable def getVersion(self): return self.version def addUsage(self): self.usage_count += 1 def addPotentialUsage(self): self.has_potential_usages = True def addRelease(self): self.has_releases = True def addNameUsage(self): self.usage_count += 1 self.has_name_usages = True def onValueEscape(self): self.is_escaped = True def isEscaped(self): return self.is_escaped def hasDefiniteUsages(self): return self.usage_count > 0 def getDefiniteUsages(self): return self.usage_count def hasPotentialUsages(self): return self.has_potential_usages def hasNameUsages(self): return self.has_name_usages def getPrevious(self): return self.previous @staticmethod def isAssignTrace(): return False @staticmethod def isUninitTrace(): return False @staticmethod def isInitTrace(): return False @staticmethod def isUnknownTrace(): return False @staticmethod def isMergeTrace(): return False def mustHaveValue(self): # TODO: Temporarily disable far reaching of assumptions, until value # escaping can be trusted. if self.variable.isModuleVariable() or \ self.variable.isSharedTechnically(): return False # Merge traces have this overloaded. return self.isInitTrace() or self.isAssignTrace() def mustNotHaveValue(self): if self.variable.isModuleVariable() or \ self.variable.isSharedTechnically(): return False return self.isUninitTrace() def getReplacementNode(self, usage): # Virtual method, pylint: disable=R0201,W0613 return None
class VariableTraceBase: @InstanceCounters.counted_init def __init__(self, variable, version, previous): self.variable = variable self.version = version # List of references. self.usages = [] # List of releases of the node. self.releases = [] # If not None, this indicates the last usage, where the value was not # yet escaped. If it is 0, it escaped immediately. Escaping is a one # time action. self.escaped_at = None # Previous trace this is replacing. self.previous = previous __del__ = InstanceCounters.counted_del() def getVariable(self): return self.variable def getVersion(self): return self.version def addUsage(self, ref_node): self.usages.append(ref_node) def addRelease(self, release_node): self.releases.append(release_node) def onValueEscape(self): self.escaped_at = len(self.usages) def isEscaped(self): return self.escaped_at is not None def getDefiniteUsages(self): return self.usages def getPrevious(self): return self.previous def getReleases(self): return self.releases @staticmethod def isAssignTrace(): return False @staticmethod def isUninitTrace(): return False @staticmethod def isInitTrace(): return False @staticmethod def isUnknownTrace(): return False @staticmethod def isMergeTrace(): return False def mustHaveValue(self): # TODO: Temporarily disable far reaching of assumptions, until value # escaping can be trusted. if self.variable.isModuleVariable() or \ self.variable.isSharedTechnically(): return False # Merge traces have this overloaded. return self.isInitTrace() or self.isAssignTrace() def mustNotHaveValue(self): if self.variable.isModuleVariable() or \ self.variable.isSharedTechnically(): return False return self.isUninitTrace()
class Variable: @InstanceCounters.counted_init def __init__(self, owner, variable_name): assert type(variable_name) is str, variable_name assert type(owner) not in (tuple, list), owner assert owner.getFullName self.variable_name = variable_name self.owner = owner self.version_number = 0 self.global_trace = None __del__ = InstanceCounters.counted_del() def getName(self): return self.variable_name def getOwner(self): return self.owner def getGlobalVariableTrace(self): # Monkey patched later to then use it, pylint: disable=R0201 return None def _getGlobalVariableTrace(self): return self.global_trace def setGlobalVariableTrace(self, global_trace): self.global_trace = global_trace def getCodeName(self): var_name = self.variable_name var_name = var_name.replace('.', '$') var_name = Utils.encodeNonAscii(var_name) return var_name def allocateTargetNumber(self): self.version_number += 1 return self.version_number # pylint: disable=R0201 def isLocalVariable(self): return False def isMaybeLocalVariable(self): return False def isParameterVariable(self): return False def isModuleVariable(self): return False def isTempVariable(self): return False # pylint: enable=R0201 def isSharedTechnically(self): from nuitka.VariableRegistry import isSharedTechnically return isSharedTechnically(self)
class VariableTraceBase(object): # We are going to have many instance attributes, pylint: disable=too-many-instance-attributes __slots__ = ("owner", "variable", "version", "usage_count", "has_potential_usages", "name_usages", "closure_usages", "is_escaped", "previous") @InstanceCounters.counted_init def __init__(self, owner, variable, version, previous): self.owner = owner self.variable = variable self.version = version # Definite usage indicator. self.usage_count = 0 # Potential usages indicator that an assignment value may be used. self.has_potential_usages = False # If 0, this indicates, the variable name needs to be assigned as name. self.name_usages = 0 self.closure_usages = False # If False, this indicates that the value is not yet escaped. self.is_escaped = False # Previous trace this is replacing. self.previous = previous __del__ = InstanceCounters.counted_del() def getVariable(self): return self.variable def getOwner(self): return self.owner def getVersion(self): return self.version def addClosureUsage(self): self.addUsage() self.closure_usages = True def addUsage(self): self.usage_count += 1 def addPotentialUsage(self): self.has_potential_usages = True def addNameUsage(self): self.usage_count += 1 self.name_usages += 1 def onValueEscape(self): self.is_escaped = True def isEscaped(self): return self.is_escaped def hasDefiniteUsages(self): return self.usage_count > 0 def getDefiniteUsages(self): return self.usage_count def hasPotentialUsages(self): return self.has_potential_usages def getNameUsageCount(self): return self.name_usages def getPrevious(self): return self.previous def getPickedCType(self, context): """ Return type to use for specific context. """ user = context.getEntryPoint() owner = self.variable.getEntryPoint() if owner is user: if self.variable.isSharedTechnically(): result = CTypeCellObject else: if enable_bool_ctype: shapes = self.variable.getTypeShapes() if len(shapes) > 1: return CTypePyObjectPtr else: assert shapes, self return shapes.pop().getCType() else: return CTypePyObjectPtr elif context.isForDirectCall(): if self.variable.isSharedTechnically(): result = CTypeCellObject else: result = CTypePyObjectPtrPtr else: result = CTypeCellObject return result @staticmethod def isAssignTrace(): return False @staticmethod def isUninitTrace(): return False @staticmethod def isInitTrace(): return False @staticmethod def isUnknownTrace(): return False @staticmethod def isMergeTrace(): return False def mustHaveValue(self): # TODO: Temporarily disable far reaching of assumptions, until value # escaping can be trusted. if self.variable.isModuleVariable() or \ self.variable.isSharedTechnically() is not False: return False # Merge traces have this overloaded. return self.isInitTrace() or self.isAssignTrace() def mustNotHaveValue(self): if self.variable.isModuleVariable() or \ self.variable.isSharedTechnically() is not False: return False return self.isUninitTrace() def getReplacementNode(self, usage): # Virtual method, pylint: disable=no-self-use,unused-argument return None def hasShapeDictionaryExact(self): # Virtual method, pylint: disable=no-self-use return False