Exemplo n.º 1
0
    def degradePartiallyFromCode(self, statement_sequence):
        from nuitka.tree.Extractions import getVariablesWritten

        variable_writes = getVariablesWritten(
            statement_sequence
        )

        # Mark all variables as unknown that are written in the statement
        # sequence, so it destroys the assumptions for final block. TODO: To
        # unknown is a bit harsh, in case it is known assigned before and
        # not deleted.
        for variable, _variable_version in variable_writes:
            self.markActiveVariableAsUnknown(
                variable = variable
            )
Exemplo n.º 2
0
    def computeLoopBody(self, trace_collection):
        # Rather complex stuff, pylint: disable=too-many-branches,too-many-locals,too-many-statements
        abort_context = trace_collection.makeAbortStackContext(
            catch_breaks=True,
            catch_continues=True,
            catch_returns=False,
            catch_exceptions=False,
        )

        has_initial = False

        with abort_context:
            loop_body = self.getLoopBody()

            if loop_body is not None:
                # Look ahead. what will be written and degrade to initial loop
                # traces about that if we are in the first iteration, later we
                # will have more precise knowledge.
                if self.loop_variables is None:
                    early = True

                    loop_variables = getVariablesWritten(loop_body)

                    self.loop_variables = {}
                    self.loop_memory = {}

                    for loop_variable in loop_variables:
                        self.loop_variables[loop_variable] = set()
                        self.loop_memory[loop_variable] = None
                else:
                    early = False

                loop_entry_traces = set()

                # List of variables to remove. TODO: Benchmark if it has any
                # value to avoid creating the normally empty list.
                to_remove = None

                # Mark all variables as loop wrap around that are written in
                # the loop and hit a 'continue'.
                for loop_variable, current in self.loop_variables.items():
                    # Loop variable became unused.
                    if not current and not early:
                        if to_remove is None:
                            to_remove = []
                        to_remove.append(loop_variable)
                        continue

                    last_ones = self.loop_memory[loop_variable]

                    if last_ones is not True:
                        last_ones = (
                            self.loop_memory[loop_variable]
                            == self.loop_variables[loop_variable]
                        )

                    loop_entry_traces.add(
                        (
                            loop_variable,
                            trace_collection.markActiveVariableAsLoopMerge(
                                variable=loop_variable,
                                shapes=self.loop_variables[loop_variable],
                                initial=last_ones is not True,
                            ),
                        )
                    )

                    has_initial = has_initial or last_ones is not True

                    if last_ones is not True:
                        self.loop_memory[loop_variable] = set(
                            self.loop_variables[loop_variable]
                        )

                if to_remove is not None:
                    for loop_variable in to_remove:
                        del self.loop_memory[loop_variable]
                        del self.loop_variables[loop_variable]

                # Forget all iterator and other value status.
                trace_collection.resetValueStates()

                result = loop_body.computeStatementsSequence(
                    trace_collection=trace_collection
                )

                # Might be changed.
                if result is not loop_body:
                    self.setLoopBody(result)
                    loop_body = result

                if loop_body is not None:
                    # Emulate terminal continue if not aborting.
                    if not loop_body.isStatementAborting():
                        trace_collection.onLoopContinue()

                continue_collections = trace_collection.getLoopContinueCollections()

                for variable, loop_entry_trace in loop_entry_traces:
                    loop_end_traces = set()

                    for continue_collection in continue_collections:
                        loop_end_trace = continue_collection.getVariableCurrentTrace(
                            variable
                        )

                        if loop_end_trace is not loop_entry_trace:
                            loop_end_trace.getTypeShape().emitAlternatives(
                                self.loop_variables[variable].add
                            )

                            loop_end_traces.add(loop_end_trace)

                    if loop_end_traces:
                        loop_entry_trace.addLoopContinueTraces(loop_end_traces)
                    else:
                        loop_entry_trace.markLoopTraceComplete()

            # If we break, the outer collections becomes a merge of all those breaks
            # or just the one, if there is only one.
            break_collections = trace_collection.getLoopBreakCollections()

        if has_initial:
            trace_collection.signalChange(
                "new_expression", self.source_ref, "Loop has incomplete variable types."
            )

        return loop_body, break_collections
Exemplo n.º 3
0
    def computeLoopBody(self, constraint_collection):
        abort_context = constraint_collection.makeAbortStackContext(
            catch_breaks     = True,
            catch_continues  = True,
            catch_returns    = False,
            catch_exceptions = False,
        )

        with abort_context:
            loop_body = self.getLoopBody()

            if loop_body is not None:
                # Look ahead. what will be written and degrade about that if we
                # are in the first iteration, later we will have more precise
                # knowledge.
                if self.loop_variables is None:
                    self.loop_variables = getVariablesWritten(
                        loop_body
                    )

                loop_entry_traces = set()

                # Mark all variables as loop wrap around that are written in
                # the loop and hit a 'continue'.
                for variable in self.loop_variables:
                    loop_entry_traces.add(
                        constraint_collection.markActiveVariableAsLoopMerge(
                            variable = variable
                        )
                    )


                result = loop_body.computeStatementsSequence(
                    constraint_collection = constraint_collection
                )

                # Might be changed.
                if result is not loop_body:
                    self.setLoopBody(result)
                    loop_body = result

                if loop_body is not None:
                    # Emulate terminal continue if not aborting.
                    if not loop_body.isStatementAborting():
                        constraint_collection.onLoopContinue()

                continue_collections = constraint_collection.getLoopContinueCollections()

                self.loop_variables = set()

                for loop_entry_trace in loop_entry_traces:
                    variable = loop_entry_trace.getVariable()

                    loop_end_traces = set()

                    for continue_collection in continue_collections:
                        loop_end_trace = continue_collection.getVariableCurrentTrace(variable)

                        if loop_end_trace is not loop_entry_trace:
                            loop_end_traces.add(loop_end_trace)

                    if loop_end_traces:
                        loop_entry_trace.addLoopContinueTraces(loop_end_traces)
                        self.loop_variables.add(variable)

            # If we break, the outer collections becomes a merge of all those breaks
            # or just the one, if there is only one.
            break_collections = constraint_collection.getLoopBreakCollections()

        return loop_body, break_collections
Exemplo n.º 4
0
    def computeStatement(self, constraint_collection):
        loop_body = self.getLoopBody()

        if loop_body is not None:
            # Look ahead. what will be written.
            variable_writes = getVariablesWritten(loop_body)

            # Mark all variables as unknown that are written in the loop body,
            # so it destroys the assumptions for loop turn around.
            for variable, _variable_version in variable_writes:
                constraint_collection.markActiveVariableAsUnknown(
                    variable = variable
                )

            result = loop_body.computeStatementsSequence(
                constraint_collection = constraint_collection
            )

            # Might be changed.
            if result is not loop_body:
                self.setLoopBody(result)
                loop_body = result

        # Consider trailing "continue" statements, these have no effect, so we
        # can remove them.
        if loop_body is not None:
            assert loop_body.isStatementsSequence()

            statements = loop_body.getStatements()
            assert statements # Cannot be empty

            # If the last statement is a "continue" statement, it can simply
            # be discarded.
            last_statement = statements[-1]
            if last_statement.isStatementContinueLoop():
                if len(statements) == 1:
                    self.setLoopBody(None)
                    loop_body = None
                else:
                    last_statement.replaceWith(None)

                constraint_collection.signalChange(
                    "new_statements",
                    last_statement.getSourceReference(),
                    """\
Removed useless terminal 'continue' as last statement of loop."""
                )

        # Consider leading "break" statements, they should be the only, and
        # should lead to removing the whole loop statement. Trailing "break"
        # statements could also be handled, but that would need to consider if
        # there are other "break" statements too. Numbering loop exits is
        # nothing we have yet.
        if loop_body is not None:
            assert loop_body.isStatementsSequence()

            statements = loop_body.getStatements()
            assert statements # Cannot be empty

            if len(statements) == 1 and statements[-1].isStatementBreakLoop():
                return None, "new_statements", """\
Removed useless loop with immediate 'break' statement."""

        return self, None, None
Exemplo n.º 5
0
    def computeExpressionRaw(self, constraint_collection):
        # The tried block must be considered as a branch, if it is not empty
        # already.
        tried_statement_sequence = self.getBlockTry()

        # May be "None" from the outset, so guard against that, later in this
        # function we are going to remove it.
        if tried_statement_sequence is not None:
            result = tried_statement_sequence.computeStatementsSequence(
                constraint_collection = constraint_collection
            )

            # Might be changed.
            if result is not tried_statement_sequence:
                tried_statement_sequence.replaceWith(result)
                tried_statement_sequence = result

        # The main expression itself.
        constraint_collection.onExpression(self.getExpression())

        final_statement_sequence = self.getBlockFinal()

        # TODO: The final must not assume that all of tried was executed,
        # instead it may have aborted after any part of it, which is a rather
        # complex definition.

        if final_statement_sequence is not None:
            if tried_statement_sequence is not None:
                from nuitka.tree.Extractions import getVariablesWritten

                variable_writes = getVariablesWritten(
                    tried_statement_sequence
                )


                # Mark all variables as unknown that are written in the tried
                # block, so it destroys the assumptions for loop turn around.
                for variable, _variable_version in variable_writes:
                    constraint_collection.markActiveVariableAsUnknown(
                        variable = variable
                    )


            # Then assuming no exception, the no raise block if present.
            result = final_statement_sequence.computeStatementsSequence(
                constraint_collection = constraint_collection
            )

            if result is not final_statement_sequence:
                self.setBlockFinal(result)

                final_statement_sequence = result

        if tried_statement_sequence is None and \
           final_statement_sequence is None:
            # If the tried and final block is empty, go to the expression
            # directly.
            return self.getExpression(), "new_expression", """\
Removed try/finally expression with empty tried and final block."""
        else:
            # TODO: Can't really merge it yet.
            constraint_collection.removeAllKnowledge()

            # Otherwise keep it as it.
            return self, None, None
Exemplo n.º 6
0
    def _computeLoopBody(self, trace_collection):
        # Rather complex stuff, pylint: disable=too-many-branches,too-many-locals

        loop_body = self.getLoopBody()
        if loop_body is None:
            return None, None, None

        # Track if we got incomplete knowledge due to loop. If so, we are not done, even
        # if no was optimization done, once we are complete, they can come.
        has_incomplete = False

        # Look ahead. what will be written and degrade to initial loop
        # traces about that if we are in the first iteration, later we
        # will have more precise knowledge.

        if self.loop_variables is None:
            self.loop_variables = getVariablesWritten(loop_body)

            # Only important to mark these states as different, so we start with
            # initial loop traces.
            for loop_variable in self.loop_variables:
                self.loop_start[loop_variable] = None

                self.loop_previous_end[loop_variable] = None
                self.loop_end[loop_variable] = set()

            first_pass = True
        else:
            first_pass = False

        # Mark all variables as loop wrap around that are written in the loop and
        # hit a 'continue' and make them become loop merges. We will strive to
        # reduce self.loop_variables if we find ones that have no change in all
        # 'continue' exits.
        loop_entry_traces = set()
        for loop_variable in self.loop_variables:
            current = trace_collection.getVariableCurrentTrace(loop_variable)

            if first_pass:
                incomplete = True
                has_incomplete = True
            else:
                incomplete = (
                    self.loop_start[loop_variable].getLoopTypeShapes()
                    != current.getLoopTypeShapes()
                    or self.loop_end[loop_variable]
                    != self.loop_previous_end[loop_variable]
                )

                if incomplete:
                    has_incomplete = True

            # TODO: We should be able to avoid these, but it breaks assumptions for assertions
            # of asssigned and deleted values in the loop.
            # if (
            #     incomplete
            #     and not current.isUninitTrace()
            #     or self.loop_end[loop_variable]
            # ):
            loop_entry_traces.add(
                (
                    loop_variable,
                    trace_collection.markActiveVariableAsLoopMerge(
                        variable=loop_variable,
                        shapes=self.loop_end[loop_variable],
                        incomplete=incomplete,
                        first_pass=first_pass,
                    ),
                )
            )

            # Remember what we started with, so we can detect changes from outside the
            # loop and make them restart the collection process.
            self.loop_start[loop_variable] = current

        abort_context = trace_collection.makeAbortStackContext(
            catch_breaks=True,
            catch_continues=True,
            catch_returns=False,
            catch_exceptions=False,
        )

        with abort_context:
            # Forget all iterator and other value status. TODO: These should be using
            # more proper tracing to benefit.
            trace_collection.resetValueStates()

            result = loop_body.computeStatementsSequence(
                trace_collection=trace_collection
            )

            # Might be changed.
            if result is not loop_body:
                self.setLoopBody(result)
                loop_body = result

            if loop_body is not None:
                # Emulate terminal continue if not aborting.
                if not loop_body.isStatementAborting():
                    trace_collection.onLoopContinue()

            continue_collections = trace_collection.getLoopContinueCollections()

            # Rebuild this with only the ones that actually changed in the loop.
            self.loop_variables = set()

            for loop_variable, loop_entry_trace in loop_entry_traces:
                loop_end_traces = set()

                if not first_pass:
                    self.loop_previous_end[loop_variable] = self.loop_end[loop_variable]
                    self.loop_end[loop_variable] = set()

                for continue_collection in continue_collections:
                    loop_end_trace = continue_collection.getVariableCurrentTrace(
                        loop_variable
                    )

                    if loop_end_trace is not loop_entry_trace:
                        if not first_pass:
                            loop_end_trace.getTypeShape().emitAlternatives(
                                self.loop_end[loop_variable].add
                            )

                        loop_end_traces.add(loop_end_trace)

                if loop_end_traces:
                    loop_entry_trace.addLoopContinueTraces(loop_end_traces)
                    self.loop_variables.add(loop_variable)

            # If we break, the outer collections becomes a merge of all those breaks
            # or just the one, if there is only one.
            break_collections = trace_collection.getLoopBreakCollections()

        if has_incomplete:
            trace_collection.signalChange(
                "new_expression", self.source_ref, "Loop has incomplete variable types."
            )

        return loop_body, break_collections, continue_collections