Esempio n. 1
0
def optimizeUnusedClosureVariables(function_body):
    for closure_variable in function_body.getClosureVariables():
        # print "VAR", closure_variable

        variable_traces = function_body.constraint_collection.getVariableTraces(
            variable=closure_variable)

        empty = areEmptyTraces(variable_traces)
        if empty:
            signalChange("var_usage",
                         function_body.getSourceReference(),
                         message="Remove unused closure variable.")

            function_body.removeClosureVariable(closure_variable)
        else:
            read_only = areReadOnlyTraces(variable_traces)

            if read_only:
                global_trace = VariableRegistry.getGlobalVariableTrace(
                    closure_variable)

                if global_trace is not None:
                    if not global_trace.hasWritesOutsideOf(function_body):
                        function_body.demoteClosureVariable(closure_variable)

                        signalChange(
                            "var_usage",
                            function_body.getSourceReference(),
                            message=
                            "Turn read-only usage of unassigned closure variable to local variable."
                        )
Esempio n. 2
0
def optimizeUnusedClosureVariables(function_body):
    for closure_variable in function_body.getClosureVariables():
        # print "VAR", closure_variable

        variable_traces = function_body.constraint_collection.getVariableTraces(
            variable = closure_variable
        )

        empty = areEmptyTraces(variable_traces)
        if empty:
            signalChange(
                "var_usage",
                function_body.getSourceReference(),
                message = "Remove unused closure variable."
            )

            function_body.removeClosureVariable(closure_variable)
        else:
            read_only = areReadOnlyTraces(variable_traces)

            if read_only:
                global_trace = VariableRegistry.getGlobalVariableTrace(closure_variable)

                if global_trace is not None:
                    if not global_trace.hasWritesOutsideOf(function_body):
                        function_body.demoteClosureVariable(closure_variable)

                        signalChange(
                            "var_usage",
                            function_body.getSourceReference(),
                            message = "Turn read-only usage of unassigned closure variable to local variable."
                        )
Esempio n. 3
0
    def computeExpression(self, constraint_collection):
        variable = self.variable

        assert variable is not None

        self.variable_trace = constraint_collection.getVariableCurrentTrace(
            variable = variable
        )

        replacement = self.variable_trace.getReplacementNode(self)

        if replacement is not None:
            return (
                replacement,
                "new_expression",
                "Value propagated for '%s' from '%s'." % (
                    self.variable.getName(),
                    replacement.getSourceReference().getAsString()
                )
            )

        self.global_trace = VariableRegistry.getGlobalVariableTrace(variable)

        # TODO: Maybe local variables are factored into this strangely.
        if self.global_trace is None and variable.isModuleVariable():
            constraint_collection.assumeUnclearLocals()
        elif (variable.isModuleVariable() and not self.global_trace.hasDefiniteWrites() ) or \
             variable.isMaybeLocalVariable():
            if self.variable_name in Builtins.builtin_exception_names:
                from .BuiltinRefNodes import ExpressionBuiltinExceptionRef

                new_node = ExpressionBuiltinExceptionRef(
                    exception_name = self.variable_name,
                    source_ref     = self.getSourceReference()
                )

                change_tags = "new_builtin_ref"
                change_desc = """\
Module variable '%s' found to be built-in exception reference.""" % (
                    self.variable_name
                )
            elif self.variable_name in Builtins.builtin_names and \
                 self.variable_name != "pow":
                from .BuiltinRefNodes import ExpressionBuiltinRef

                new_node = ExpressionBuiltinRef(
                    builtin_name = self.variable_name,
                    source_ref   = self.getSourceReference()
                )

                change_tags = "new_builtin_ref"
                change_desc = """\
Module variable '%s' found to be built-in reference.""" % (
                    self.variable_name
                )
            elif self.variable_name == "__name__":
                new_node = ExpressionConstantRef(
                    constant   = variable.getOwner().getParentModule().\
                                   getFullName(),
                    source_ref = self.getSourceReference()
                )

                change_tags = "new_constant"
                change_desc = """\
Replaced read-only module attribute '__name__' with constant value."""
            elif self.variable_name == "__package__":
                new_node = ExpressionConstantRef(
                    constant   = variable.getOwner().getPackage(),
                    source_ref = self.getSourceReference()
                )

                change_tags = "new_constant"
                change_desc = """\
Replaced read-only module attribute '__package__' with constant value."""
            else:
                self.variable_trace.addUsage()

                # Probably should give a warning once about it.
                new_node = self
                change_tags = None
                change_desc = None

            return new_node, change_tags, change_desc

        self.variable_trace.addUsage()

        return self, None, None
Esempio n. 4
0
    def computeStatement(self, constraint_collection):
        # This is very complex stuff, pylint: disable=R0912

        # TODO: Way too ugly to have global trace kinds just here, and needs to
        # be abstracted somehow. But for now we let it live here: pylint: disable=R0911,R0915

        # Assignment source may re-compute here:
        constraint_collection.onExpression(self.getAssignSource())
        source = self.getAssignSource()

        # No assignment will occur, if the assignment source raises, so strip it
        # away.
        if source.willRaiseException(BaseException):

            result = makeStatementExpressionOnlyReplacementNode(expression=source, node=self)

            return (
                result,
                "new_raise",
                """\
Assignment raises exception in assigned value, removed assignment.""",
            )

        variable_ref = self.getTargetVariableRef()
        variable = variable_ref.getVariable()

        # Not allowed anymore at this point.
        assert variable is not None

        # Assigning from and to the same variable, can be optimized away
        # immediately, there is no point in doing it. Exceptions are of course
        # module variables that collide with built-in names.
        if not variable.isModuleVariable() and source.isExpressionVariableRef() and source.getVariable() == variable:

            # A variable access that has a side effect, must be preserved,
            # so it can e.g. raise an exception, otherwise we can be fully
            # removed.
            if source.mayHaveSideEffects():
                result = makeStatementExpressionOnlyReplacementNode(expression=source, node=self)

                return (
                    result,
                    "new_statements",
                    """\
Reduced assignment of variable from itself to access of it.""",
                )
            else:
                return (
                    None,
                    "new_statements",
                    """\
Removed assignment of variable from itself which is known to be defined.""",
                )

        # If the assignment source has side effects, we can simply evaluate them
        # beforehand, we have already visited and evaluated them before.
        if source.isExpressionSideEffects():
            statements = [
                makeStatementExpressionOnlyReplacementNode(side_effect, self) for side_effect in source.getSideEffects()
            ]

            statements.append(self)

            parent = self.parent
            result = makeStatementsSequenceReplacementNode(statements=statements, node=self)
            result.parent = parent

            # Need to update it.
            self.setAssignSource(source.getExpression())
            source = self.getAssignSource()

            return (
                result,
                "new_statements",
                """\
Side effects of assignments promoted to statements.""",
            )

        # Set-up the trace to the trace collection, so future references will
        # find this assignment.
        self.variable_trace = constraint_collection.onVariableSet(assign_node=self)

        global_trace = VariableRegistry.getGlobalVariableTrace(variable)

        if global_trace is not None and Options.isExperimental():
            last_trace = global_trace.getMatchingAssignTrace(self)

            if last_trace is not None:
                if variable.isLocalVariable() or variable.isTempVariable():
                    if source.isCompileTimeConstant():

                        # Can safely forward propagate only non-mutable constants.
                        if not source.isMutable():
                            provider = self.getParentVariableProvider()

                            if variable.isTempVariable() or (
                                not provider.isUnoptimized() and not provider.isClassDictCreation()
                            ):

                                if last_trace.hasDefiniteUsages():
                                    self.variable_trace.setReplacementNode(lambda usage: source.makeClone())
                                    propagated = True
                                else:
                                    propagated = False

                                if not last_trace.hasPotentialUsages() and not last_trace.hasNameUsages():
                                    # This limitation may fall later.
                                    if not variable.isSharedLogically():

                                        if not last_trace.getPrevious().isUninitTrace():
                                            # TODO: We could well decide, if that's even necessary.
                                            result = StatementDelVariable(
                                                variable_ref=self.getTargetVariableRef(),
                                                tolerant=True,
                                                source_ref=self.getSourceReference(),
                                            )
                                        else:
                                            result = None

                                        return (
                                            result,
                                            "new_statements",
                                            "Dropped %s assignment statement to '%s'."
                                            % (
                                                "propagated" if propagated else "dead",
                                                self.getTargetVariableRef().getVariableName(),
                                            ),
                                        )
                        else:
                            # Something might be possible still.

                            pass
                    elif (
                        Options.isExperimental()
                        and source.isExpressionFunctionCreation()
                        and not source.getFunctionRef().getFunctionBody().isGenerator()
                        and not source.getFunctionRef().getFunctionBody().isClassDictCreation()
                        and not source.getDefaults()
                        and not source.getKwDefaults()
                        and not source.getAnnotations()
                    ):
                        # TODO: These are very mutable, right?

                        provider = self.getParentVariableProvider()

                        if variable.isTempVariable() or (
                            not provider.isUnoptimized() and not provider.isClassDictCreation()
                        ):

                            # This limitation may fall later.
                            if not variable.isSharedLogically():

                                if (
                                    last_trace.getDefiniteUsages() <= 1
                                    and not last_trace.hasPotentialUsages()
                                    and not last_trace.hasNameUsages()
                                ):

                                    if last_trace.getDefiniteUsages() == 1:
                                        self.variable_trace.setReplacementNode(lambda usage: source.makeClone())
                                        propagated = True
                                    else:
                                        propagated = False

                                    if not last_trace.getPrevious().isUninitTrace():
                                        # TODO: We could well decide, if that's even necessary.
                                        result = StatementDelVariable(
                                            variable_ref=self.getTargetVariableRef(),
                                            tolerant=True,
                                            source_ref=self.getSourceReference(),
                                        )
                                    else:
                                        result = None

                                    return (
                                        result,
                                        "new_statements",
                                        "Dropped %s assignment statement to '%s'."
                                        % (
                                            "propagated" if propagated else "dead",
                                            self.getTargetVariableRef().getVariableName(),
                                        ),
                                    )

                    else:
                        # More cases thinkable.
                        pass

        return self, None, None
Esempio n. 5
0
    def computeExpression(self, constraint_collection):
        variable = self.variable

        assert variable is not None

        self.variable_trace = constraint_collection.getVariableCurrentTrace(
            variable = variable
        )

        global_trace = VariableRegistry.getGlobalVariableTrace(variable)

        # TODO: Maybe local variables are factored into this strangely.
        if global_trace is None and variable.isModuleVariable():
            constraint_collection.assumeUnclearLocals()
        elif (variable.isModuleVariable() and not global_trace.hasDefiniteWrites() ) or \
             variable.isMaybeLocalVariable():
            if self.variable_name in Builtins.builtin_exception_names:
                from .BuiltinRefNodes import ExpressionBuiltinExceptionRef

                new_node = ExpressionBuiltinExceptionRef(
                    exception_name = self.variable_name,
                    source_ref     = self.getSourceReference()
                )

                change_tags = "new_builtin_ref"
                change_desc = """\
Module variable '%s' found to be built-in exception reference.""" % (
                    self.variable_name
                )
            elif self.variable_name in Builtins.builtin_names and \
                 self.variable_name != "pow":
                from .BuiltinRefNodes import ExpressionBuiltinRef

                new_node = ExpressionBuiltinRef(
                    builtin_name = self.variable_name,
                    source_ref   = self.getSourceReference()
                )

                change_tags = "new_builtin_ref"
                change_desc = """\
Module variable '%s' found to be built-in reference.""" % (
                    self.variable_name
                )
            elif self.variable_name == "__name__":
                new_node = ExpressionConstantRef(
                    constant   = variable.getOwner().getParentModule().\
                                   getFullName(),
                    source_ref = self.getSourceReference()
                )

                change_tags = "new_constant"
                change_desc = """\
Replaced read-only module attribute '__name__' with constant value."""
            elif self.variable_name == "__package__":
                new_node = ExpressionConstantRef(
                    constant   = variable.getOwner().getPackage(),
                    source_ref = self.getSourceReference()
                )

                change_tags = "new_constant"
                change_desc = """\
Replaced read-only module attribute '__package__' with constant value."""
            else:
                # Probably should give a warning once about it.
                new_node = self
                change_tags = None
                change_desc = None

            return new_node, change_tags, change_desc

        return self, None, None
Esempio n. 6
0
    def computeStatement(self, constraint_collection):
        # Assignment source may re-compute here:
        constraint_collection.onExpression(self.getAssignSource())
        source = self.getAssignSource()

        # No assignment will occur, if the assignment source raises, so strip it
        # away.
        if source.willRaiseException(BaseException):

            result = makeStatementExpressionOnlyReplacementNode(
                expression = source,
                node       = self
            )

            return result, "new_raise", """\
Assignment raises exception in assigned value, removed assignment."""

        variable_ref = self.getTargetVariableRef()
        variable = variable_ref.getVariable()

        # Not allowed anymore at this point.
        assert variable is not None

        # Assigning from and to the same variable, can be optimized away
        # immediately, there is no point in doing it. Exceptions are of course
        # module variables that collide with built-in names.
        if not variable.isModuleVariable() and \
             source.isExpressionVariableRef() and \
             source.getVariable() == variable:

            # A variable access that has a side effect, must be preserved,
            # so it can e.g. raise an exception, otherwise we can be fully
            # removed.
            if source.mayHaveSideEffects():
                result = makeStatementExpressionOnlyReplacementNode(
                    expression = source,
                    node       = self
                )

                return result, "new_statements", """\
Reduced assignment of variable from itself to access of it."""
            else:
                return None, "new_statements", """\
Removed assignment of variable from itself which is known to be defined."""


        # If the assignment source has side effects, we can simply evaluate them
        # beforehand, we have already visited and evaluated them before.
        if source.isExpressionSideEffects():
            statements = [
                makeStatementExpressionOnlyReplacementNode(
                    side_effect,
                    self
                )
                for side_effect in
                source.getSideEffects()
            ]

            statements.append(self)

            parent = self.parent
            result = makeStatementsSequenceReplacementNode(
                statements = statements,
                node       = self,
            )
            result.parent = parent

            # Need to update it.
            self.setAssignSource(source.getExpression())
            source = self.getAssignSource()

            result = result, "new_statements", """\
Side effects of assignments promoted to statements."""
        else:
            result = self, None, None

        # Set-up the trace to the trace collection, so future references will
        # find this assignment.
        self.variable_trace = constraint_collection.onVariableSet(
            assign_node = self
        )

        global_trace = VariableRegistry.getGlobalVariableTrace(variable)

        if global_trace is not None:
            if variable.isTempVariable():
                if source.isCompileTimeConstant() and not source.isMutable():
                    self.variable_trace.setReplacementNode(source)
            elif variable.isLocalVariable():
                if source.isCompileTimeConstant() and not source.isMutable():
                    provider = self.getParentVariableProvider()

                    if provider.isPythonModule() or \
                       (not provider.isUnoptimized() and not provider.isClassDictCreation()):
                        self.variable_trace.setReplacementNode(source)

        return result
Esempio n. 7
0
    def computeExpression(self, constraint_collection):
        variable = self.variable

        assert variable is not None

        self.variable_trace = constraint_collection.getVariableCurrentTrace(
            variable=variable)

        replacement = self.variable_trace.getReplacementNode(self)

        if replacement is not None:
            constraint_collection.signalChange(
                "new_expression", self.source_ref,
                "Value propagated for '%s' from '%s'." %
                (self.variable.getName(),
                 replacement.getSourceReference().getAsString()))

            # Need to compute the replacement still.
            return replacement.computeExpression(constraint_collection)

        if not self.variable_trace.mustHaveValue():
            # TODO: This could be way more specific surely.
            constraint_collection.onExceptionRaiseExit(BaseException)

        self.global_trace = VariableRegistry.getGlobalVariableTrace(variable)

        # TODO: Maybe local variables are factored into this strangely.
        if self.global_trace is None and variable.isModuleVariable():
            constraint_collection.assumeUnclearLocals()
        elif (variable.isModuleVariable() and not self.global_trace.hasDefiniteWrites() ) or \
             variable.isMaybeLocalVariable():
            if self.variable_name in Builtins.builtin_exception_names:
                from .BuiltinRefNodes import ExpressionBuiltinExceptionRef

                new_node = ExpressionBuiltinExceptionRef(
                    exception_name=self.variable_name,
                    source_ref=self.getSourceReference())

                change_tags = "new_builtin_ref"
                change_desc = """\
Module variable '%s' found to be built-in exception reference.""" % (
                    self.variable_name)
            elif self.variable_name in Builtins.builtin_names and \
                 self.variable_name != "pow":
                from .BuiltinRefNodes import ExpressionBuiltinRef

                new_node = ExpressionBuiltinRef(
                    builtin_name=self.variable_name,
                    source_ref=self.getSourceReference())

                change_tags = "new_builtin_ref"
                change_desc = """\
Module variable '%s' found to be built-in reference.""" % (self.variable_name)
            elif self.variable_name == "__name__":
                new_node = ExpressionConstantRef(
                    constant   = variable.getOwner().getParentModule().\
                                   getFullName(),
                    source_ref = self.getSourceReference()
                )

                change_tags = "new_constant"
                change_desc = """\
Replaced read-only module attribute '__name__' with constant value."""
            elif self.variable_name == "__package__":
                new_node = ExpressionConstantRef(
                    constant=variable.getOwner().getPackage(),
                    source_ref=self.getSourceReference())

                change_tags = "new_constant"
                change_desc = """\
Replaced read-only module attribute '__package__' with constant value."""
            else:
                self.variable_trace.addUsage()

                # Probably should give a warning once about it.
                new_node = self
                change_tags = None
                change_desc = None

            return new_node, change_tags, change_desc

        self.variable_trace.addUsage()

        return self, None, None