Optimization: Faster variable trace maintenance

* We now trace variables in trace collection as a dictionary
  per variable with a dictionary of the versions, this is
  closer to out frequent usage per variable.

* That makes it a lot easier to update variables after the
  tracing is finished to know their users and writers.

* Requires a lot less work, but also makes work less memory
  local such that the performance gain is relatively small
  despite less work being done.

* Also avoids that a set for the users is to be maintained.
This commit is contained in:
Kay Hayen
2025-11-18 09:55:13 +01:00
parent ec8e28efef
commit b4e603ab7c
7 changed files with 211 additions and 216 deletions

View File

@@ -156,7 +156,7 @@ class SourceCodeReferenceInternal(SourceCodeReference):
return True
def fromFilename(filename):
def makeSourceReferenceFromFilename(filename):
return SourceCodeReference.fromFilenameAndLine(filename=filename, line=1)

View File

@@ -51,11 +51,10 @@ class Variable(getMetaClassBase("Variable", require_slots=True)):
self.shared_users = False
self.traces = set()
self.traces = {}
# Derived from all traces.
self.users = None
self.writers = None
self.writers = set()
if isCountingInstances():
__del__ = counted_del()
@@ -145,12 +144,12 @@ class Variable(getMetaClassBase("Variable", require_slots=True)):
if not self.shared_users:
return False
if not self.users:
if not self.traces:
return False
owner = self.owner.getEntryPoint()
for user in self.users:
for user in self.traces:
user = user.getEntryPoint()
while user is not owner and (
@@ -164,58 +163,65 @@ class Variable(getMetaClassBase("Variable", require_slots=True)):
return False
def addTrace(self, variable_trace):
self.traces.add(variable_trace)
def setTracesForUserFirst(self, user, variable_traces):
self.traces[user] = variable_traces
def removeTrace(self, variable_trace):
self.traces.remove(variable_trace)
for trace in variable_traces.values():
if trace.isAssignTrace():
self.writers.add(user)
def getTraces(self):
"""For debugging only"""
return self.traces
break
if user is not self.owner and trace.isDeletedTrace():
self.writers.add(user)
def hasEmptyTracesFor(self, owner):
break
def setTracesForUserUpdate(self, user, variable_traces):
self.traces[user] = variable_traces
if user in self.writers:
for trace in variable_traces.values():
if trace.isAssignTrace():
break
if user is not self.owner and trace.isDeletedTrace():
break
else:
self.writers.remove(user)
def removeTracesForUser(self, user):
del self.traces[user]
if user in self.writers:
self.writers.remove(user)
def hasEmptyTracesFor(self, user):
"""Do these traces contain any usage."""
for trace in self.traces:
if trace.owner is owner and trace.isUsingTrace():
return False
if user in self.traces:
for trace in self.traces[user].values():
if trace.isUsingTrace():
return False
return True
def hasNoWritingTraces(self):
"""Do these traces contain any writes."""
for trace in self.traces:
if trace.isWritingTrace():
return False
for traces in self.traces.values():
for trace in traces.values():
if trace.isWritingTrace():
return False
return True
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 owner is not self.owner and trace.isDeletedTrace():
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:
elif not self.traces:
return False
elif provider in self.users:
return len(self.users) > 1
elif provider in self.traces:
return len(self.traces) > 1
else:
return bool(self.users)
return True
def hasWritersOutsideOf(self, provider):
if not self.owner.locals_scope.complete:
@@ -225,45 +231,50 @@ class Variable(getMetaClassBase("Variable", require_slots=True)):
):
return False
return None
elif self.writers is None:
elif not self.writers:
return False
elif provider in self.writers:
return len(self.writers) > 1
else:
return bool(self.writers)
return True
def getMatchingUnescapedAssignTrace(self, assign_node):
found = None
for trace in self.traces:
if trace.isAssignTrace():
if trace.getAssignNode() is assign_node:
found = trace
elif trace.isEscapeTrace():
return None
for traces in self.traces.values():
for trace in traces.values():
if trace.isAssignTrace():
if trace.getAssignNode() is assign_node:
found = trace
elif trace.isEscapeTrace():
return None
if found is not None:
return found
return found
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.isEscapeTrace():
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
for traces in self.traces.values():
for trace in traces.values():
if trace.isAssignTrace():
result.add(trace.getAssignNode().getTypeShape())
elif trace.isUnknownTrace():
result.add(tshape_unknown)
elif trace.isEscapeTrace():
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
@@ -442,49 +453,58 @@ class LocalsDictVariable(Variable):
return trace_collection.initVariableUnknown(self)
def _updateVariablesFromCollectionFirst(new_collection):
for variable, variable_traces in iterItems(new_collection.getVariableTracesAll()):
variable.setTracesForUserFirst(new_collection.owner, variable_traces)
# Release the memory, and prevent the "active" state from being ever
# inspected, it's useless now.
new_collection.variable_actives.clear()
del new_collection.variable_actives
def updateVariablesFromCollection(old_collection, new_collection, source_ref):
# After removing/adding traces, we need to pre-compute the users state
# information.
touched_variables = set()
loop_trace_removal = set()
if old_collection is None:
return _updateVariablesFromCollectionFirst(new_collection)
old_traces = old_collection.getVariableTracesAll()
new_traces = new_collection.getVariableTracesAll()
owner = new_collection.owner
# Release the memory, and prevent the "active" state from being ever
# inspected, it's useless now.
new_collection.variable_actives.clear()
del new_collection.variable_actives
for variable, variable_traces in iterItems(new_traces):
variable.setTracesForUserUpdate(owner, variable_traces)
for variable in old_traces:
if variable not in new_traces:
variable.removeTracesForUser(owner)
if old_collection.loop_variables != new_collection.loop_variables:
new_collection.signalChange(
"var_usage",
source_ref,
lambda: "Loop variable '%s' usage ceased."
% ",".join(
sorted(
variable.getName()
for variable in (
old_collection.loop_variables - new_collection.loop_variables
)
)
),
)
def removeVariablesFromCollection(old_collection):
if old_collection is not None:
for (variable, _version), variable_trace in iterItems(
old_collection.getVariableTracesAll()
):
variable.removeTrace(variable_trace)
touched_variables.add(variable)
owner = old_collection.owner
if variable_trace.isLoopTrace():
loop_trace_removal.add(variable)
if new_collection is not None:
for (variable, _version), variable_trace in iterItems(
new_collection.getVariableTracesAll()
):
variable.addTrace(variable_trace)
touched_variables.add(variable)
if variable_trace.isLoopTrace():
if variable in loop_trace_removal:
loop_trace_removal.remove(variable)
# Release the memory, and prevent the "active" state from being ever
# inspected, it's useless now.
new_collection.variable_actives.clear()
del new_collection.variable_actives
for variable in touched_variables:
variable.updateUsageState()
if loop_trace_removal:
if new_collection is not None:
new_collection.signalChange(
"var_usage",
source_ref,
lambda: "Loop variable '%s' usage ceased."
% ",".join(variable.getName() for variable in loop_trace_removal),
)
for variable in old_collection.getVariableTracesAll():
variable.removeTracesForUser(owner)
# To detect the Python2 shared variable deletion, that would be a syntax

View File

@@ -34,6 +34,7 @@ from nuitka.Errors import NuitkaCodeDeficit
from nuitka.plugins.Plugins import Plugins
from nuitka.PythonFlavors import isNuitkaPython
from nuitka.PythonVersions import python_version
from nuitka.SourceCodeReferences import makeSourceReferenceFromFilename
from nuitka.Tracing import recursion_logger
from nuitka.tree.ReformulationMultidist import locateMultidistModule
from nuitka.utils.AppDirs import getCacheDir
@@ -1079,14 +1080,14 @@ def decideModuleSourceRef(filename, module_name, is_main, is_fake, logger):
if is_fake:
source_filename = filename
source_ref = SourceCodeReferences.fromFilename(filename=filename)
source_ref = makeSourceReferenceFromFilename(filename=filename)
module_name = is_fake
elif os.path.isfile(filename):
source_filename = filename
source_ref = SourceCodeReferences.fromFilename(filename=filename)
source_ref = makeSourceReferenceFromFilename(filename=filename)
elif isPackageDir(filename):
is_package = True
@@ -1094,12 +1095,10 @@ def decideModuleSourceRef(filename, module_name, is_main, is_fake, logger):
source_filename = getNormalizedPath(os.path.join(filename, "__init__.py"))
if not os.path.isfile(source_filename):
source_ref = SourceCodeReferences.fromFilename(
filename=filename
).atInternal()
source_ref = makeSourceReferenceFromFilename(filename=filename).atInternal()
is_namespace = True
else:
source_ref = SourceCodeReferences.fromFilename(
source_ref = makeSourceReferenceFromFilename(
filename=getNormalizedPath(os.path.abspath(source_filename))
)

View File

@@ -18,7 +18,7 @@ classes.
import inspect
import re
from nuitka import Options, Variables
from nuitka import Options
from nuitka.Constants import isMutable
from nuitka.optimizations.TraceCollections import (
TraceCollectionPureFunction,
@@ -35,6 +35,7 @@ from nuitka.tree.Extractions import updateVariableUsage
from nuitka.tree.SourceHandling import readSourceLines
from nuitka.tree.TreeHelpers import makeDictCreationOrConstant2
from nuitka.utils.CStrings import decodePythonIdentifierFromC
from nuitka.Variables import LocalVariable, updateVariablesFromCollection
from .ChildrenHavingMixins import (
ChildHavingBodyOptionalMixin,
@@ -234,13 +235,9 @@ class ExpressionFunctionBodyBase(
assert variable.getOwner() is not self
new_variable = Variables.LocalVariable(
owner=self, variable_name=variable.getName()
)
for variable_trace in variable.traces:
if variable_trace.getOwner() is self:
new_variable.addTrace(variable_trace)
new_variable.updateUsageState()
new_variable = LocalVariable(owner=self, variable_name=variable.getName())
if self in variable.traces:
new_variable.setTracesForUserFirst(self, variable.traces[self])
self.locals_scope.unregisterClosureVariable(variable)
self.locals_scope.registerProvidedVariable(new_variable)
@@ -489,7 +486,7 @@ class ExpressionFunctionEntryPointBase(EntryPointMixin, ExpressionFunctionBodyBa
self.computeFunction(trace_collection)
trace_collection.updateVariablesFromCollection(old_collection, self.source_ref)
updateVariablesFromCollection(old_collection, trace_collection, self.source_ref)
def computeFunction(self, trace_collection):
statements_sequence = self.subnode_body
@@ -838,8 +835,8 @@ class ExpressionFunctionPureBody(ExpressionFunctionBody):
with withChangeIndicationsTo(mySignal):
self.computeFunction(trace_collection)
trace_collection.updateVariablesFromCollection(
old_collection, self.source_ref
updateVariablesFromCollection(
old_collection, trace_collection, self.source_ref
)
if tags:

View File

@@ -9,15 +9,19 @@ together and cross-module optimizations are the most difficult to tackle.
import os
from nuitka import Options, Variables
from nuitka.containers.OrderedSets import OrderedSet
from nuitka.importing.Importing import locateModule, makeModuleUsageAttempt
from nuitka.importing.Recursion import decideRecursion, recurseTo
from nuitka.ModuleRegistry import getModuleByName, getOwnerFromCodeName
from nuitka.optimizations.TraceCollections import TraceCollectionModule
from nuitka.Options import hasPythonFlagIsolated
from nuitka.Options import (
getFileReferenceMode,
hasPythonFlagIsolated,
hasPythonFlagPackageMode,
shallMakeModule,
)
from nuitka.PythonVersions import python_version
from nuitka.SourceCodeReferences import fromFilename
from nuitka.SourceCodeReferences import makeSourceReferenceFromFilename
from nuitka.tree.SourceHandling import parsePyIFile, readSourceCodeFromFilename
from nuitka.utils.CStrings import encodePythonIdentifierToC
from nuitka.utils.FileOperations import switchFilenameExtension
@@ -26,6 +30,7 @@ from nuitka.utils.Importing import (
getPackageDirFilename,
)
from nuitka.utils.ModuleNames import ModuleName
from nuitka.Variables import ModuleVariable, updateVariablesFromCollection
from .ChildrenHavingMixins import (
ModuleChildrenHavingBodyOptionalStatementsOrNoneFunctionsTupleMixin,
@@ -174,7 +179,7 @@ class PythonModuleBase(NodeBase):
return result
def getRunTimeFilename(self):
reference_mode = Options.getFileReferenceMode()
reference_mode = getFileReferenceMode()
if reference_mode == "original":
return self.getCompileTimeFilename()
@@ -442,7 +447,7 @@ class CompiledPythonModule(
def createProvidedVariable(self, variable_name):
assert variable_name not in self.variables
result = Variables.ModuleVariable(module=self, variable_name=variable_name)
result = ModuleVariable(module=self, variable_name=variable_name)
self.variables[variable_name] = result
@@ -604,8 +609,8 @@ class CompiledPythonModule(
)
# Finalize locals scopes previously determined for removal in last pass.
self.trace_collection.updateVariablesFromCollection(
old_collection=old_collection, source_ref=self.source_ref
updateVariablesFromCollection(
old_collection, self.trace_collection, self.source_ref
)
# Indicate if this is pass 1 for the module as return value.
@@ -711,7 +716,7 @@ class CompiledPythonModule(
return None
def getRuntimeNameValue(self):
if self.isMainModule() and Options.hasPythonFlagPackageMode():
if self.isMainModule() and hasPythonFlagPackageMode():
return "__main__"
elif self.module_name.isMultidistModuleName():
return "__main__"
@@ -766,7 +771,7 @@ class CompiledPythonNamespacePackage(CompiledPythonPackage):
def makeUncompiledPythonModule(
module_name, reason, filename, bytecode, is_package, technical
):
source_ref = fromFilename(filename)
source_ref = makeSourceReferenceFromFilename(filename)
if is_package:
return UncompiledPythonPackage(
@@ -872,7 +877,7 @@ class PythonMainModule(CompiledPythonModule):
__slots__ = ("main_added", "standard_library_modules")
def __init__(self, module_name, main_added, mode, future_spec, source_ref):
assert not Options.shallMakeModule()
assert not shallMakeModule()
# Is this one from a "__main__.py" file
self.main_added = main_added

View File

@@ -13,7 +13,6 @@ import contextlib
from collections import defaultdict
from contextlib import contextmanager
from nuitka import Variables
from nuitka.__past__ import iterItems # Python3 compatibility.
from nuitka.containers.OrderedDicts import OrderedDict
from nuitka.containers.OrderedSets import OrderedSet
@@ -82,27 +81,15 @@ class CollectionUpdateMixin(object):
# Mixins are not allowed to specify slots.
__slots__ = ()
def hasVariableTrace(self, variable, version):
return (variable, version) in self.variable_traces
def getVariableTrace(self, variable, version):
return self.variable_traces[(variable, version)]
def getVariableTracesAll(self):
return self.variable_traces
def addVariableTrace(self, variable, version, trace):
key = variable, version
assert key not in self.variable_traces, (key, self)
self.variable_traces[key] = trace
def addVariableMergeMultipleTrace(self, variable, traces):
version = variable.allocateTargetNumber()
trace_merge = ValueTraceMerge(traces)
self.addVariableTrace(variable, version, trace_merge)
version = variable.allocateTargetNumber()
self.variable_traces[variable][version] = trace_merge
return version
@@ -113,11 +100,11 @@ class CollectionStartPointMixin(CollectionUpdateMixin):
These are modules, functions, etc. typically entry points.
"""
# Many things are traced, pylint: disable=too-many-instance-attributes
# Mixins are not allowed to specify slots, pylint: disable=assigning-non-slot
__slots__ = ()
# Many things are traced
def __init__(self):
# Variable assignments performed in here, last issued number, only used
# to determine the next number that should be used for a new assignment.
@@ -125,7 +112,7 @@ class CollectionStartPointMixin(CollectionUpdateMixin):
# The full trace of a variable with a version for the function or module
# this is.
self.variable_traces = {}
self.variable_traces = defaultdict(dict)
self.break_collections = None
self.continue_collections = None
@@ -134,6 +121,10 @@ class CollectionStartPointMixin(CollectionUpdateMixin):
self.outline_functions = None
# What loop variables were there, them going away is something we want
# to know.
self.loop_variables = set()
def getLoopBreakCollections(self):
return self.break_collections
@@ -197,10 +188,6 @@ class CollectionStartPointMixin(CollectionUpdateMixin):
def getExceptionRaiseCollections(self):
return self.exception_collections
# TODO: Eliminate this function, call it directly where this is used.
def updateVariablesFromCollection(self, old_collection, source_ref):
Variables.updateVariablesFromCollection(old_collection, self, source_ref)
@contextlib.contextmanager
def makeAbortStackContext(
self, catch_breaks, catch_continues, catch_returns, catch_exceptions
@@ -327,7 +314,7 @@ class TraceCollectionBase(object):
lazy so to keep the tracing branches minimal where possible.
"""
return self.getVariableTrace(variable, self.variable_actives[variable])
return self.variable_traces[variable][self.variable_actives[variable]]
def hasVariableCurrentTrace(self, variable):
return variable in self.variable_actives
@@ -359,52 +346,43 @@ class TraceCollectionBase(object):
def markActiveVariableAsEscaped(self, variable):
version = self.variable_actives[variable]
current = self.getVariableTrace(variable, version)
variable_traces = self.variable_traces[variable]
current = variable_traces[version]
if current.isTraceThatNeedsEscape():
# Escape traces are div 3 rem 1.
version = version // 3 * 3 + 1
if not self.hasVariableTrace(variable, version):
self.addVariableTrace(
variable,
version,
ValueTraceEscaped(self.owner, current),
)
if version not in variable_traces:
variable_traces[version] = ValueTraceEscaped(self.owner, current)
self.markCurrentVariableTrace(variable, version)
def markClosureVariableAsUnknown(self, variable):
version = self.variable_actives[variable]
current = self.getVariableTrace(variable, version)
variable_traces = self.variable_traces[variable]
current = variable_traces[version]
if not current.isUnknownTrace():
# Unknown traces are div 3 rem 2.
version = version // 3 * 3 + 2
if not self.hasVariableTrace(variable, version):
self.addVariableTrace(
variable,
version,
ValueTraceUnknown(self.owner, current),
)
if version not in variable_traces:
variable_traces[version] = ValueTraceUnknown(self.owner, current)
self.markCurrentVariableTrace(variable, version)
def markActiveVariableAsUnknown(self, variable):
version = self.variable_actives[variable]
current = self.getVariableTrace(variable, version)
variable_traces = self.variable_traces[variable]
current = variable_traces[version]
if not current.isUnknownOrVeryTrustedTrace():
# Unknown traces are div 3 rem 2.
version = version // 3 * 3 + 2
if not self.hasVariableTrace(variable, version):
self.addVariableTrace(
variable,
version,
ValueTraceUnknown(self.owner, current),
)
if version not in variable_traces:
variable_traces[version] = ValueTraceUnknown(self.owner, current)
self.markCurrentVariableTrace(variable, version)
@@ -424,7 +402,7 @@ class TraceCollectionBase(object):
result = ValueTraceLoopComplete(loop_node, current, shapes)
version = variable.allocateTargetNumber()
self.addVariableTrace(variable, version, result)
self.variable_traces[variable][version] = result
self.markCurrentVariableTrace(variable, version)
@@ -480,7 +458,7 @@ class TraceCollectionBase(object):
self.getVariableCurrentTrace(variable),
)
self.addVariableTrace(variable, version, variable_trace)
self.variable_traces[variable][version] = variable_trace
# Make references point to it.
self.markCurrentVariableTrace(variable, version)
@@ -514,7 +492,7 @@ class TraceCollectionBase(object):
self.getVariableCurrentTrace(variable),
)
self.addVariableTrace(variable, version, variable_trace)
self.variable_traces[variable][version] = variable_trace
# Make references point to it.
self.markCurrentVariableTrace(variable, version)
@@ -528,7 +506,7 @@ class TraceCollectionBase(object):
self.getVariableCurrentTrace(variable),
)
self.addVariableTrace(variable, version, variable_trace)
self.variable_traces[variable][version] = variable_trace
# Make references point to it.
self.markCurrentVariableTrace(variable, version)
@@ -545,7 +523,7 @@ class TraceCollectionBase(object):
replacement,
)
self.addVariableTrace(variable, version, variable_trace)
self.variable_traces[variable][version] = variable_trace
# Make references point to it.
self.markCurrentVariableTrace(variable, version)
@@ -566,7 +544,7 @@ class TraceCollectionBase(object):
)
# Assign to not initialized again.
self.addVariableTrace(variable, version, variable_trace)
self.variable_traces[variable][version] = variable_trace
# Make references point to it.
self.markCurrentVariableTrace(variable, version)
@@ -765,8 +743,10 @@ class TraceCollectionBase(object):
other_version = collection2.variable_actives[variable]
if version != other_version:
trace1 = self.getVariableTrace(variable, version)
trace2 = self.getVariableTrace(variable, other_version)
variable_traces = self.variable_traces[variable]
trace1 = variable_traces[version]
trace2 = variable_traces[other_version]
if version % 3 == 1 and trace1.previous is trace2:
pass
@@ -840,8 +820,10 @@ class TraceCollectionBase(object):
escaped = []
winner_version = None
variable_traces = self.variable_traces[variable]
for version in sorted(versions):
trace = self.getVariableTrace(variable, version)
trace = variable_traces[version]
if version % 3 == 1:
winner_version = version
@@ -872,7 +854,7 @@ class TraceCollectionBase(object):
# TODO: This could be avoided, if we detect no actual changes being present, but it might
# be more costly.
self.has_unescaped_variables = False
self.has_unescaped_variables = True
def replaceBranch(self, collection_replace):
self.variable_actives = collection_replace.variable_actives
@@ -961,48 +943,51 @@ class TraceCollectionBase(object):
def initVariableUnknown(self, variable):
trace = ValueTraceUnknown(self.owner, None)
self.addVariableTrace(variable, 0, trace)
self.variable_traces[variable][0] = trace
return trace
def initVariableModule(self, variable):
trace = ValueTraceUnknown(self.owner, None)
self.addVariableTrace(variable, 0, trace)
self.variable_traces[variable][0] = trace
return trace
def initVariableInit(self, variable):
trace = ValueTraceInit(self.owner)
self.addVariableTrace(variable, 0, trace)
self.variable_traces[variable][0] = trace
return trace
def initVariableInitStarArgs(self, variable):
trace = ValueTraceInitStarArgs(self.owner)
self.addVariableTrace(variable, 0, trace)
self.variable_traces[variable][0] = trace
return trace
def initVariableInitStarDict(self, variable):
trace = ValueTraceInitStarDict(self.owner)
self.addVariableTrace(variable, 0, trace)
self.variable_traces[variable][0] = trace
return trace
def initVariableUninitialized(self, variable):
trace = ValueTraceUninitialized(self.owner, None)
self.addVariableTrace(variable, 0, trace)
self.variable_traces[variable][0] = trace
return trace
class TraceCollectionBranch(CollectionUpdateMixin, TraceCollectionBase):
__slots__ = ("variable_traces",)
__slots__ = (
"variable_traces",
"loop_variables",
)
def __init__(self, name, parent):
TraceCollectionBase.__init__(
@@ -1021,6 +1006,7 @@ class TraceCollectionBranch(CollectionUpdateMixin, TraceCollectionBase):
# For quick access without going to parent.
self.variable_traces = parent.variable_traces
self.loop_variables = parent.loop_variables
def computeBranch(self, branch):
assert branch.isStatementsSequence()
@@ -1034,7 +1020,10 @@ class TraceCollectionBranch(CollectionUpdateMixin, TraceCollectionBase):
class TraceCollectionSnapshot(CollectionUpdateMixin, TraceCollectionBase):
__slots__ = ("variable_traces",)
__slots__ = (
"variable_traces",
"loop_variables",
)
def __init__(self, name, parent):
TraceCollectionBase.__init__(
@@ -1053,12 +1042,14 @@ class TraceCollectionSnapshot(CollectionUpdateMixin, TraceCollectionBase):
# For quick access without going to parent.
self.variable_traces = parent.variable_traces
self.loop_variables = parent.loop_variables
class TraceCollectionFunction(CollectionStartPointMixin, TraceCollectionBase):
__slots__ = (
"variable_versions",
"variable_traces",
"loop_variables",
"break_collections",
"continue_collections",
"return_collections",
@@ -1149,7 +1140,7 @@ class TraceCollectionFunction(CollectionStartPointMixin, TraceCollectionBase):
None,
)
self.addVariableTrace(variable, 0, assign_trace)
self.variable_traces[variable][0] = assign_trace
return assign_trace
@@ -1177,6 +1168,7 @@ class TraceCollectionModule(CollectionStartPointMixin, TraceCollectionBase):
__slots__ = (
"variable_versions",
"variable_traces",
"loop_variables",
"break_collections",
"continue_collections",
"return_collections",
@@ -1248,24 +1240,6 @@ class TraceCollectionModule(CollectionStartPointMixin, TraceCollectionBase):
self.distribution_names[distribution_name] = success
def _checkActivesDiff(a1, a2):
result = True
for variable in a1:
if variable not in a2:
print("Only in a1", variable)
result = False
elif a1[variable] != a2[variable]:
print("Different ", variable, a1[variable], a2[variable])
result = False
for variable in a2:
if variable not in a1:
print("Only in a2", variable)
result = False
return result
# Part of "Nuitka", an optimizing Python compiler that is compatible and
# integrates with CPython, but also works on its own.
#

View File

@@ -16,9 +16,9 @@ from nuitka.nodes.FunctionNodes import (
ExpressionFunctionPureBody,
ExpressionFunctionPureInlineConstBody,
)
from nuitka.SourceCodeReferences import fromFilename
from nuitka.SourceCodeReferences import makeSourceReferenceFromFilename
internal_source_ref = fromFilename("internal").atInternal()
internal_source_ref = makeSourceReferenceFromFilename("internal").atInternal()
def once_decorator(func):