Exemplo n.º 1
0
def strategicReduction(logPrefix, infilename, lithArgs, targetTime, lev):  # pylint: disable=invalid-name
    # pylint: disable=missing-param-doc,missing-return-doc,missing-return-type-doc,missing-type-doc,too-complex
    # pylint: disable=too-many-branches,too-many-locals,too-many-statements
    """Reduce jsfunfuzz output files using Lithium by using various strategies."""
    # This is an array because Python does not like assigning to upvars.
    reductionCount = [0]  # pylint: disable=invalid-name
    backupFilename = infilename + '-backup'  # pylint: disable=invalid-name

    def lithReduceCmd(strategy):  # pylint: disable=invalid-name,missing-param-doc,missing-return-doc
        # pylint: disable=missing-return-type-doc,missing-type-doc
        """Lithium reduction commands accepting various strategies."""
        reductionCount[0] += 1
        # Remove empty elements
        fullLithArgs = [x for x in (strategy + lithArgs) if x]  # pylint: disable=invalid-name
        print(
            sps.shellify([sys.executable, "-u", "-m", "lithium"] +
                         fullLithArgs))

        desc = '-chars' if strategy == '--char' else '-lines'
        (lithResult, lithDetails) = runLithium(  # pylint: disable=invalid-name
            fullLithArgs, "%s-%s%s" % (logPrefix, reductionCount[0], desc),
            targetTime)
        if lithResult == LITH_FINISHED:
            shutil.copy2(infilename, backupFilename)

        return lithResult, lithDetails

    print()
    print("Running the first line reduction...")
    print()
    # Step 1: Run the first instance of line reduction.
    lithResult, lithDetails = lithReduceCmd([])  # pylint: disable=invalid-name

    if lithDetails is not None:  # lithDetails can be None if testcase no longer becomes interesting
        origNumOfLines = int(lithDetails.split()[0])  # pylint: disable=invalid-name

    hasTryItOut = False  # pylint: disable=invalid-name
    hasTryItOutRegex = re.compile(r'count=[0-9]+; tryItOut\("')  # pylint: disable=invalid-name

    with open(infilename, 'r') as f:
        for line in file_manipulation.linesWith(f, '; tryItOut("'):
            # Checks if testcase came from jsfunfuzz or compare_jit.
            # Do not use .match here, it only matches from the start of the line:
            # https://docs.python.org/2/library/re.html#search-vs-match
            hasTryItOut = hasTryItOutRegex.search(line)  # pylint: disable=invalid-name
            if hasTryItOut:  # Stop searching after finding the first tryItOut line.
                break

    # Step 2: Run 1 instance of 1-line reduction after moving tryItOut and count=X around.
    if lithResult == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:

        tryItOutAndCountRegex = re.compile(
            r'"\);\ncount=([0-9]+); tryItOut\("',  # pylint: disable=invalid-name
            re.MULTILINE)
        with open(infilename, 'r') as f:
            infileContents = f.read()  # pylint: disable=invalid-name
            infileContents = re.sub(
                tryItOutAndCountRegex,  # pylint: disable=invalid-name
                ';\\\n"); count=\\1; tryItOut("\\\n',
                infileContents)
        with open(infilename, 'w') as f:
            f.write(infileContents)

        print()
        print(
            "Running 1 instance of 1-line reduction after moving tryItOut and count=X..."
        )
        print()
        # --chunksize=1: Reduce only individual lines, for only 1 round.
        lithResult, lithDetails = lithReduceCmd(['--chunksize=1'])  # pylint: disable=invalid-name

    # Step 3: Run 1 instance of 2-line reduction after moving count=X to its own line and add a
    # 1-line offset.
    if lithResult == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:
        intendedLines = []  # pylint: disable=invalid-name
        with open(infilename, 'r') as f:
            for line in f:  # The testcase is likely to already be partially reduced.
                if 'dumpln(cookie' not in line:  # jsfunfuzz-specific line ignore
                    # This should be simpler than re.compile.
                    intendedLines.append(
                        line.replace('; count=', ';\ncount=').replace(
                            '; tryItOut("', ';\ntryItOut("')
                        # The 1-line offset is added here.
                        .replace('SPLICE DDBEGIN', 'SPLICE DDBEGIN\n'))

        with open(infilename, "w") as f:
            f.writelines(intendedLines)
        print()
        print(
            "Running 1 instance of 2-line reduction after moving count=X to its own line..."
        )
        print()
        lithResult, lithDetails = lithReduceCmd(['--chunksize=2'])  # pylint: disable=invalid-name

    # Step 4: Run 1 instance of 2-line reduction again, e.g. to remove pairs of STRICT_MODE lines.
    if lithResult == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:
        print()
        print("Running 1 instance of 2-line reduction again...")
        print()
        lithResult, lithDetails = lithReduceCmd(['--chunksize=2'])  # pylint: disable=invalid-name

    isLevOverallMismatchAsmJsAvailable = (
        lev == JS_OVERALL_MISMATCH and  # pylint: disable=invalid-name
        file_contains_str(infilename, 'isAsmJSCompilationAvailable'))
    # Step 5 (not always run): Run character reduction within interesting lines.
    if lithResult == LITH_FINISHED and origNumOfLines <= 50 and targetTime is None and \
            lev >= JS_OVERALL_MISMATCH and not isLevOverallMismatchAsmJsAvailable:
        print()
        print("Running character reduction...")
        print()
        lithResult, lithDetails = lithReduceCmd(['--char'])  # pylint: disable=invalid-name

    # Step 6: Run line reduction after activating SECOND DDBEGIN with a 1-line offset.
    if lithResult == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:
        infileContents = []  # pylint: disable=invalid-name
        with open(infilename, 'r') as f:
            for line in f:
                if 'NIGEBDD' in line:
                    infileContents.append(line.replace('NIGEBDD', 'DDBEGIN'))
                    infileContents.append(
                        '\n')  # The 1-line offset is added here.
                    continue
                infileContents.append(line)
        with open(infilename, 'w') as f:
            f.writelines(infileContents)

        print()
        print("Running line reduction with a 1-line offset...")
        print()
        lithResult, lithDetails = lithReduceCmd([])  # pylint: disable=invalid-name

    # Step 7: Run line reduction for a final time.
    if lithResult == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:
        print()
        print("Running the final line reduction...")
        print()
        lithResult, lithDetails = lithReduceCmd([])  # pylint: disable=invalid-name

    # Restore from backup if testcase can no longer be reproduced halfway through reduction.
    if lithResult != LITH_FINISHED and lithResult != LITH_PLEASE_CONTINUE:
        # Probably can move instead of copy the backup, once this has stabilised.
        if os.path.isfile(backupFilename):
            shutil.copy2(backupFilename, infilename)
        else:
            print("DEBUG! backupFilename is supposed to be: %s" %
                  backupFilename)

    return lithResult, lithDetails
Exemplo n.º 2
0
def reduction_strat(logPrefix, infilename, lithArgs, targetTime, lev):  # pylint: disable=invalid-name
    # pylint: disable=missing-param-doc,missing-return-doc,missing-return-type-doc,missing-type-doc,too-complex
    # pylint: disable=too-many-branches,too-many-locals,too-many-statements
    """Reduce jsfunfuzz output files using Lithium by using various strategies."""

    # This is an array because Python does not like assigning to upvars.
    reductionCount = [0]  # pylint: disable=invalid-name
    backup_file = (logPrefix.parent / (logPrefix.stem + "-backup"))

    def lith_reduce(strategy):
        """Lithium reduction commands accepting various strategies.

        Args:
            strategy (str): Intended strategy to use

        Returns:
            (tuple): The finished Lithium run result and details
        """
        reductionCount[0] += 1
        # Remove empty elements
        full_lith_args = [x for x in (strategy + lithArgs) if x]
        print(" ".join(quote(str(x)) for x in [sys.executable, "-u", "-m", "lithium"] + full_lith_args))

        desc = "-chars" if strategy == "--char" else "-lines"
        (lith_result, lith_details) = run_lithium(
            full_lith_args, (logPrefix.parent / ("%s-%s%s" % (logPrefix.stem, reductionCount[0], desc))), targetTime)
        if lith_result == LITH_FINISHED:
            shutil.copy2(str(infilename), str(backup_file))

        return lith_result, lith_details

    print()
    print("Running the first line reduction...")
    print()
    # Step 1: Run the first instance of line reduction.
    lith_result, lith_details = lith_reduce([])

    if lith_details is not None:  # lith_details can be None if testcase no longer becomes interesting
        origNumOfLines = int(lith_details.split()[0])  # pylint: disable=invalid-name

    hasTryItOut = False  # pylint: disable=invalid-name
    hasTryItOutRegex = re.compile(r'count=[0-9]+; tryItOut\("')  # pylint: disable=invalid-name

    with io.open(str(infilename), "r", encoding="utf-8", errors="replace") as f:
        for line in file_manipulation.linesWith(f, '; tryItOut("'):
            # Checks if testcase came from jsfunfuzz or compare_jit.
            # Do not use .match here, it only matches from the start of the line:
            # https://docs.python.org/2/library/re.html#search-vs-match
            hasTryItOut = hasTryItOutRegex.search(line)  # pylint: disable=invalid-name
            if hasTryItOut:  # Stop searching after finding the first tryItOut line.
                break

    # Step 2: Run 1 instance of 1-line reduction after moving tryItOut and count=X around.
    if lith_result == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:

        tryItOutAndCountRegex = re.compile(r'"\);\ncount=([0-9]+); tryItOut\("',  # pylint: disable=invalid-name
                                           re.MULTILINE)
        with io.open(str(infilename), "r", encoding="utf-8", errors="replace") as f:
            infileContents = f.read()  # pylint: disable=invalid-name
            infileContents = re.sub(tryItOutAndCountRegex,  # pylint: disable=invalid-name
                                    ';\\\n"); count=\\1; tryItOut("\\\n',
                                    infileContents)
        with io.open(str(infilename), "w", encoding="utf-8", errors="replace") as f:
            f.write(infileContents)

        print()
        print("Running 1 instance of 1-line reduction after moving tryItOut and count=X...")
        print()
        # --chunksize=1: Reduce only individual lines, for only 1 round.
        lith_result, lith_details = lith_reduce(["--chunksize=1"])

    # Step 3: Run 1 instance of 2-line reduction after moving count=X to its own line and add a
    # 1-line offset.
    if lith_result == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:
        intendedLines = []  # pylint: disable=invalid-name
        with io.open(str(infilename), "r", encoding="utf-8", errors="replace") as f:
            for line in f:  # The testcase is likely to already be partially reduced.
                if "dumpln(cookie" not in line:  # jsfunfuzz-specific line ignore
                    # This should be simpler than re.compile.
                    intendedLines.append(line.replace("; count=", ";\ncount=")
                                         .replace('; tryItOut("', ';\ntryItOut("')
                                         # The 1-line offset is added here.
                                         .replace("SPLICE DDBEGIN", "SPLICE DDBEGIN\n"))

        with io.open(str(infilename), "w", encoding="utf-8", errors="replace") as f:
            f.writelines(intendedLines)
        print()
        print("Running 1 instance of 2-line reduction after moving count=X to its own line...")
        print()
        lith_result, lith_details = lith_reduce(["--chunksize=2"])

    # Step 4: Run 1 instance of 2-line reduction again, e.g. to remove pairs of STRICT_MODE lines.
    if lith_result == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:
        print()
        print("Running 1 instance of 2-line reduction again...")
        print()
        lith_result, lith_details = lith_reduce(["--chunksize=2"])

    isLevOverallMismatchAsmJsAvailable = (lev == JS_OVERALL_MISMATCH and  # pylint: disable=invalid-name
                                          file_contains_str(str(infilename), "isAsmJSCompilationAvailable"))
    # Step 5 (not always run): Run character reduction within interesting lines.
    if lith_result == LITH_FINISHED and origNumOfLines <= 50 and targetTime is None and \
            lev >= JS_OVERALL_MISMATCH and not isLevOverallMismatchAsmJsAvailable:
        print()
        print("Running character reduction...")
        print()
        lith_result, lith_details = lith_reduce(["--char"])

    # Step 6: Run line reduction after activating SECOND DDBEGIN with a 1-line offset.
    if lith_result == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:
        infileContents = []  # pylint: disable=invalid-name
        with io.open(str(infilename), "r", encoding="utf-8", errors="replace") as f:
            for line in f:
                if "NIGEBDD" in line:
                    infileContents.append(line.replace("NIGEBDD", "DDBEGIN"))
                    infileContents.append("\n")  # The 1-line offset is added here.
                    continue
                infileContents.append(line)
        with io.open(str(infilename), "w", encoding="utf-8", errors="replace") as f:
            f.writelines(infileContents)

        print()
        print("Running line reduction with a 1-line offset...")
        print()
        lith_result, lith_details = lith_reduce([])

    # Step 7: Run line reduction for a final time.
    if lith_result == LITH_FINISHED and origNumOfLines <= 50 and hasTryItOut and lev >= JS_VG_AMISS:
        print()
        print("Running the final line reduction...")
        print()
        lith_result, lith_details = lith_reduce([])

    # Restore from backup if testcase can no longer be reproduced halfway through reduction.
    if lith_result != LITH_FINISHED:
        # Probably can move instead of copy the backup, once this has stabilised.
        if backup_file.is_file():
            shutil.copy2(str(backup_file), str(infilename))
        else:
            print("DEBUG! backup_file is supposed to be: %s" % backup_file)

    return lith_result, lith_details